summary refs log tree commit
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml5
-rw-r--r--.yardopts2
-rw-r--r--Gemfile2
-rw-r--r--README.rdoc3
-rw-r--r--SPEC78
-rw-r--r--lib/rack.rb1
-rw-r--r--lib/rack/builder.rb4
-rw-r--r--lib/rack/chunked.rb13
-rw-r--r--lib/rack/conditionalget.rb5
-rw-r--r--lib/rack/content_length.rb6
-rw-r--r--lib/rack/deflater.rb6
-rw-r--r--lib/rack/etag.rb8
-rw-r--r--lib/rack/file.rb12
-rw-r--r--lib/rack/handler.rb4
-rw-r--r--lib/rack/handler/webrick.rb18
-rw-r--r--lib/rack/head.rb9
-rw-r--r--lib/rack/lint.rb94
-rw-r--r--lib/rack/lobster.rb11
-rw-r--r--lib/rack/methodoverride.rb15
-rw-r--r--lib/rack/mock.rb9
-rw-r--r--lib/rack/multipart.rb2
-rw-r--r--lib/rack/multipart/parser.rb11
-rw-r--r--lib/rack/multipart/uploaded_file.rb4
-rw-r--r--lib/rack/request.rb52
-rw-r--r--lib/rack/response.rb4
-rw-r--r--lib/rack/sendfile.rb13
-rw-r--r--lib/rack/server.rb48
-rw-r--r--lib/rack/session/memcache.rb8
-rw-r--r--lib/rack/session/pool.rb9
-rw-r--r--lib/rack/showexceptions.rb31
-rw-r--r--lib/rack/tempfile_reaper.rb22
-rw-r--r--lib/rack/urlmap.rb20
-rw-r--r--lib/rack/utils.rb61
-rw-r--r--test/multipart/filename_and_no_name6
-rw-r--r--test/spec_chunked.rb16
-rw-r--r--test/spec_content_length.rb4
-rw-r--r--test/spec_etag.rb13
-rw-r--r--test/spec_head.rb2
-rw-r--r--test/spec_lobster.rb2
-rw-r--r--test/spec_mock.rb8
-rw-r--r--test/spec_multipart.rb36
-rw-r--r--test/spec_request.rb100
-rw-r--r--test/spec_response.rb13
-rw-r--r--test/spec_server.rb20
-rw-r--r--test/spec_showexceptions.rb65
-rw-r--r--test/spec_tempfile_reaper.rb63
-rw-r--r--test/spec_urlmap.rb23
-rw-r--r--test/spec_utils.rb58
-rw-r--r--test/spec_webrick.rb18
50 files changed, 825 insertions, 213 deletions
diff --git a/.gitignore b/.gitignore
index ae1afb4f..7a7ad3a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ Gemfile.lock
 .rbx
 doc
 /.bundle
+/.yardoc
diff --git a/.travis.yml b/.travis.yml
index 56ed952d..aa2eca43 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,8 +9,9 @@ rvm:
   - 1.9.2
   - 1.9.3
   - 2.0.0
-  - 2.1.0-preview2
-  - rbx
+  - 2.1
+  - ruby-head
+  - rbx-2
   - jruby
   - ree
 notifications:
diff --git a/.yardopts b/.yardopts
new file mode 100644
index 00000000..f4d6aeba
--- /dev/null
+++ b/.yardopts
@@ -0,0 +1,2 @@
+-
+SPEC
diff --git a/Gemfile b/Gemfile
index 6f0697b2..e3583c56 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,7 +3,7 @@ source 'https://rubygems.org'
 gemspec
 
 group :extra do
-  gem 'ruby-fcgi'
+  gem 'fcgi'
   gem 'memcache-client'
   gem 'mongrel', '>= 1.2.0.pre2'
   gem 'thin'
diff --git a/README.rdoc b/README.rdoc
index 7a3c8d58..d5bd6d2e 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,4 +1,4 @@
-= 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, a modular Ruby webserver interface {<img src="https://secure.travis-ci.org/rack/rack.svg" alt="Build Status" />}[http://travis-ci.org/rack/rack] {<img src="https://gemnasium.com/rack/rack.svg" 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
@@ -33,6 +33,7 @@ These web servers include Rack handlers in their distributions:
 * Unicorn
 * unixrack
 * uWSGI
+* yahns
 * Zbatery
 
 Any valid Rack app will run the same on all these handlers, without
diff --git a/SPEC b/SPEC
index f7bfb3df..0deb57b5 100644
--- a/SPEC
+++ b/SPEC
@@ -40,7 +40,17 @@ below.
 <tt>QUERY_STRING</tt>:: The portion of the request URL that
                         follows the <tt>?</tt>, if any. May be
                         empty, but is always required!
-<tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL.  <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required.
+<tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>::
+                       When combined with <tt>SCRIPT_NAME</tt> and
+                       <tt>PATH_INFO</tt>, these variables can be
+                       used to complete the URL. Note, however,
+                       that <tt>HTTP_HOST</tt>, if present,
+                       should be used in preference to
+                       <tt>SERVER_NAME</tt> for reconstructing
+                       the request URL.
+                       <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt>
+                       can never be empty strings, and so
+                       are always required.
 <tt>HTTP_</tt> Variables:: Variables corresponding to the
                            client-supplied HTTP request
                            headers (i.e., variables whose
@@ -49,24 +59,47 @@ below.
                            variables should correspond with
                            the presence or absence of the
                            appropriate HTTP header in the
-                           request. See <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
-                           RFC3875 section 4.1.18</a> for specific behavior.
+                           request. See
+                           <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
+                           RFC3875 section 4.1.18</a> for
+                           specific behavior.
 In addition to this, the Rack environment must include these
 Rack-specific variables:
-<tt>rack.version</tt>:: The Array representing this version of Rack. See Rack::VERSION, that corresponds to the version of this SPEC.
-<tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
+<tt>rack.version</tt>:: The Array representing this version of Rack
+                        See Rack::VERSION, that corresponds to
+                        the version of this SPEC.
+<tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the
+                           request URL.
 <tt>rack.input</tt>:: See below, the input stream.
 <tt>rack.errors</tt>:: See below, the error stream.
-<tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
-<tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
-<tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
-<tt>rack.hijack?</tt>:: present and true if the server supports connection hijacking. See below, hijacking.
-<tt>rack.hijack</tt>:: an object responding to #call that must be called at least once before using rack.hijack_io. It is recommended #call return rack.hijack_io as well as setting it in env if necessary.
-<tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack has received #call, this will contain an object resembling an IO. See hijacking.
+<tt>rack.multithread</tt>:: true if the application object may be
+                            simultaneously invoked by another thread
+                            in the same process, false otherwise.
+<tt>rack.multiprocess</tt>:: true if an equivalent application object
+                             may be simultaneously invoked by another
+                             process, false otherwise.
+<tt>rack.run_once</tt>:: true if the server expects
+                         (but does not guarantee!) that the
+                         application will only be invoked this one
+                         time during the life of its containing
+                         process. Normally, this will only be true
+                         for a server based on CGI
+                         (or something similar).
+<tt>rack.hijack?</tt>:: present and true if the server supports
+                        connection hijacking. See below, hijacking.
+<tt>rack.hijack</tt>:: an object responding to #call that must be
+                       called at least once before using
+                       rack.hijack_io.
+                       It is recommended #call return rack.hijack_io
+                       as well as setting it in env if necessary.
+<tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack
+                          has received #call, this will contain
+                          an object resembling an IO. See hijacking.
 Additional environment specifications have approved to
 standardized middleware APIs.  None of these are required to
 be implemented by the server.
-<tt>rack.session</tt>:: A hash like interface for storing request session data.
+<tt>rack.session</tt>:: A hash like interface for storing
+                        request session data.
                         The store must implement:
                         store(key, value)         (aliased as []=);
                         fetch(key, default = nil) (aliased as []);
@@ -110,15 +143,18 @@ must be opened in binary mode, for Ruby 1.9 compatibility.
 The input stream must respond to +gets+, +each+, +read+ and +rewind+.
 * +gets+ must be called without arguments and return a string,
   or +nil+ on EOF.
-* +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
-  If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must
-  be a String and may not be nil. If +length+ is given and not nil, then this method
-  reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
-  then this method reads all data until EOF.
-  When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
-  if +length+ is not given or is nil.
-  If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
-  newly created String object.
+* +read+ behaves like IO#read.
+  Its signature is <tt>read([length, [buffer]])</tt>.
+  If given, +length+ must be a non-negative Integer (>= 0) or +nil+,
+  and +buffer+ must be a String and may not be nil.
+  If +length+ is given and not nil, then this method reads at most
+  +length+ bytes from the input stream.
+  If +length+ is not given or nil, then this method reads
+  all data until EOF.
+  When EOF is reached, this method returns nil if +length+ is given
+  and not nil, or "" if +length+ is not given or is nil.
+  If +buffer+ is given, then the read data will be placed
+  into +buffer+ instead of a newly created String object.
 * +each+ must be called without arguments and only yield Strings.
 * +rewind+ must be called without arguments. It rewinds the input
   stream back to the beginning. It must not raise Errno::ESPIPE:
diff --git a/lib/rack.rb b/lib/rack.rb
index 57119df3..341514c5 100644
--- a/lib/rack.rb
+++ b/lib/rack.rb
@@ -53,6 +53,7 @@ module Rack
   autoload :ShowExceptions, "rack/showexceptions"
   autoload :ShowStatus, "rack/showstatus"
   autoload :Static, "rack/static"
+  autoload :TempfileReaper, "rack/tempfile_reaper"
   autoload :URLMap, "rack/urlmap"
   autoload :Utils, "rack/utils"
   autoload :Multipart, "rack/multipart"
diff --git a/lib/rack/builder.rb b/lib/rack/builder.rb
index fa3a1ea9..bda3be27 100644
--- a/lib/rack/builder.rb
+++ b/lib/rack/builder.rb
@@ -73,7 +73,7 @@ module Rack
     #   end
     #
     #   use Middleware
-    #   run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] }
+    #   run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
     #
     # All requests through to this application will first be processed by the middleware class.
     # The +call+ method in this example sets an additional environment key which then can be
@@ -157,7 +157,7 @@ module Rack
 
     def generate_map(default_app, mapping)
       mapped = default_app ? {'/' => default_app} : {}
-      mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b) }
+      mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b).to_app }
       URLMap.new(mapped)
     end
   end
diff --git a/lib/rack/chunked.rb b/lib/rack/chunked.rb
index a400756a..ea221fa9 100644
--- a/lib/rack/chunked.rb
+++ b/lib/rack/chunked.rb
@@ -39,11 +39,22 @@ module Rack
       @app = app
     end
 
+    # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
+    # a version (nor response headers)
+    def chunkable_version?(ver)
+      case ver
+      when "HTTP/1.0", nil, "HTTP/0.9"
+        false
+      else
+        true
+      end
+    end
+
     def call(env)
       status, headers, body = @app.call(env)
       headers = HeaderHash.new(headers)
 
-      if env['HTTP_VERSION'] == 'HTTP/1.0' ||
+      if ! chunkable_version?(env['HTTP_VERSION']) ||
          STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
          headers['Content-Length'] ||
          headers['Transfer-Encoding']
diff --git a/lib/rack/conditionalget.rb b/lib/rack/conditionalget.rb
index ed87c54e..88573166 100644
--- a/lib/rack/conditionalget.rb
+++ b/lib/rack/conditionalget.rb
@@ -28,7 +28,10 @@ module Rack
           status = 304
           headers.delete('Content-Type')
           headers.delete('Content-Length')
-          body = []
+          original_body = body
+          body = Rack::BodyProxy.new([]) do
+            original_body.close if original_body.respond_to?(:close)
+          end
         end
         [status, headers, body]
       else
diff --git a/lib/rack/content_length.rb b/lib/rack/content_length.rb
index 634bdc41..71bc919b 100644
--- a/lib/rack/content_length.rb
+++ b/lib/rack/content_length.rb
@@ -1,4 +1,5 @@
 require 'rack/utils'
+require 'rack/body_proxy'
 
 module Rack
 
@@ -22,7 +23,10 @@ module Rack
         obody = body
         body, length = [], 0
         obody.each { |part| body << part; length += bytesize(part) }
-        obody.close if obody.respond_to?(:close)
+
+        body = BodyProxy.new(body) do
+          obody.close if obody.respond_to?(:close)
+        end
 
         headers['Content-Length'] = length.to_s
       end
diff --git a/lib/rack/deflater.rb b/lib/rack/deflater.rb
index 638bf049..9df510bd 100644
--- a/lib/rack/deflater.rb
+++ b/lib/rack/deflater.rb
@@ -65,9 +65,9 @@ module Rack
       when "identity"
         [status, headers, body]
       when nil
-        body.close if body.respond_to?(:close)
         message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
-        [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]]
+        bp = Rack::BodyProxy.new([message]) { body.close if body.respond_to?(:close) }
+        [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, bp]
       end
     end
 
@@ -87,7 +87,6 @@ module Rack
           gzip.flush
         }
       ensure
-        close
         gzip.close
         @writer = nil
       end
@@ -123,7 +122,6 @@ module Rack
         yield deflator.finish
         nil
       ensure
-        close
         deflator.close
       end
 
diff --git a/lib/rack/etag.rb b/lib/rack/etag.rb
index 5fa09abd..fefe671f 100644
--- a/lib/rack/etag.rb
+++ b/lib/rack/etag.rb
@@ -23,8 +23,12 @@ module Rack
       status, headers, body = @app.call(env)
 
       if etag_status?(status) && etag_body?(body) && !skip_caching?(headers)
-        digest, body = digest_body(body)
-        headers['ETag'] = %("#{digest}") if digest
+        original_body = body
+        digest, new_body = digest_body(body)
+        body = Rack::BodyProxy.new(new_body) do
+          original_body.close if original_body.respond_to?(:close)
+        end
+        headers['ETag'] = %(W/"#{digest}") if digest
       end
 
       unless headers['Cache-Control']
diff --git a/lib/rack/file.rb b/lib/rack/file.rb
index 820ecd60..c8f8d0d1 100644
--- a/lib/rack/file.rb
+++ b/lib/rack/file.rb
@@ -12,7 +12,6 @@ module Rack
   # like sendfile on the +path+.
 
   class File
-    SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
     ALLOWED_VERBS = %w[GET HEAD OPTIONS]
     ALLOW_HEADER = ALLOWED_VERBS.join(', ')
 
@@ -40,16 +39,9 @@ module Rack
       end
 
       path_info = Utils.unescape(env["PATH_INFO"])
-      parts = path_info.split SEPS
+      clean_path_info = Utils.clean_path_info(path_info)
 
-      clean = []
-
-      parts.each do |part|
-        next if part.empty? || part == '.'
-        part == '..' ? clean.pop : clean << part
-      end
-
-      @path = F.join(@root, *clean)
+      @path = F.join(@root, clean_path_info)
 
       available = begin
         F.file?(@path) && F.readable?(@path)
diff --git a/lib/rack/handler.rb b/lib/rack/handler.rb
index 8f649974..b5fa0268 100644
--- a/lib/rack/handler.rb
+++ b/lib/rack/handler.rb
@@ -53,9 +53,9 @@ module Rack
         Rack::Handler::FastCGI
       elsif ENV.include?("REQUEST_METHOD")
         Rack::Handler::CGI
-     elsif ENV.include?("RACK_HANDLER")
+      elsif ENV.include?("RACK_HANDLER")
         self.get(ENV["RACK_HANDLER"])
-     else
+      else
         pick ['thin', 'puma', 'webrick']
       end
     end
diff --git a/lib/rack/handler/webrick.rb b/lib/rack/handler/webrick.rb
index f76679b4..023d8b27 100644
--- a/lib/rack/handler/webrick.rb
+++ b/lib/rack/handler/webrick.rb
@@ -2,6 +2,23 @@ require 'webrick'
 require 'stringio'
 require 'rack/content_length'
 
+# This monkey patch allows for applications to perform their own chunking
+# through WEBrick::HTTPResponse iff rack is set to true.
+class WEBrick::HTTPResponse
+  attr_accessor :rack
+
+  alias _rack_setup_header setup_header
+  def setup_header
+    app_chunking = rack && @header['transfer-encoding'] == 'chunked'
+
+    @chunked = app_chunking if app_chunking
+
+    _rack_setup_header
+
+    @chunked = false if app_chunking
+  end
+end
+
 module Rack
   module Handler
     class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
@@ -39,6 +56,7 @@ module Rack
       end
 
       def service(req, res)
+        res.rack = true
         env = req.meta_vars
         env.delete_if { |k, v| v.nil? }
 
diff --git a/lib/rack/head.rb b/lib/rack/head.rb
index 7ffead6c..72f3dbdd 100644
--- a/lib/rack/head.rb
+++ b/lib/rack/head.rb
@@ -1,3 +1,5 @@
+require 'rack/body_proxy'
+
 module Rack
 
 class Head
@@ -11,8 +13,11 @@ class Head
     status, headers, body = @app.call(env)
 
     if env["REQUEST_METHOD"] == "HEAD"
-      body.close if body.respond_to? :close
-      [status, headers, []]
+      [
+        status, headers, Rack::BodyProxy.new([]) do
+          body.close if body.respond_to? :close
+        end
+      ]
     else
       [status, headers, body]
     end
diff --git a/lib/rack/lint.rb b/lib/rack/lint.rb
index 3978b70a..667c34a6 100644
--- a/lib/rack/lint.rb
+++ b/lib/rack/lint.rb
@@ -102,7 +102,17 @@ module Rack
       ##                         follows the <tt>?</tt>, if any. May be
       ##                         empty, but is always required!
 
-      ## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL.  <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required.
+      ## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>::
+      ##                        When combined with <tt>SCRIPT_NAME</tt> and
+      ##                        <tt>PATH_INFO</tt>, these variables can be
+      ##                        used to complete the URL. Note, however,
+      ##                        that <tt>HTTP_HOST</tt>, if present,
+      ##                        should be used in preference to
+      ##                        <tt>SERVER_NAME</tt> for reconstructing
+      ##                        the request URL.
+      ##                        <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt>
+      ##                        can never be empty strings, and so
+      ##                        are always required.
 
       ## <tt>HTTP_</tt> Variables:: Variables corresponding to the
       ##                            client-supplied HTTP request
@@ -112,29 +122,60 @@ module Rack
       ##                            variables should correspond with
       ##                            the presence or absence of the
       ##                            appropriate HTTP header in the
-      ##                            request. See <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
-      ##                            RFC3875 section 4.1.18</a> for specific behavior.
+      ##                            request. See
+      ##                            <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
+      ##                            RFC3875 section 4.1.18</a> for
+      ##                            specific behavior.
 
       ## In addition to this, the Rack environment must include these
       ## Rack-specific variables:
 
-      ## <tt>rack.version</tt>:: The Array representing this version of Rack. See Rack::VERSION, that corresponds to the version of this SPEC.
-      ## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
+      ## <tt>rack.version</tt>:: The Array representing this version of Rack
+      ##                         See Rack::VERSION, that corresponds to
+      ##                         the version of this SPEC.
+
+      ## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the
+      ##                            request URL.
+
       ## <tt>rack.input</tt>:: See below, the input stream.
+
       ## <tt>rack.errors</tt>:: See below, the error stream.
-      ## <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
-      ## <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
-      ## <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
-      ## <tt>rack.hijack?</tt>:: present and true if the server supports connection hijacking. See below, hijacking.
-      ## <tt>rack.hijack</tt>:: an object responding to #call that must be called at least once before using rack.hijack_io. It is recommended #call return rack.hijack_io as well as setting it in env if necessary.
-      ## <tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack has received #call, this will contain an object resembling an IO. See hijacking.
-      ##
+
+      ## <tt>rack.multithread</tt>:: true if the application object may be
+      ##                             simultaneously invoked by another thread
+      ##                             in the same process, false otherwise.
+
+      ## <tt>rack.multiprocess</tt>:: true if an equivalent application object
+      ##                              may be simultaneously invoked by another
+      ##                              process, false otherwise.
+
+      ## <tt>rack.run_once</tt>:: true if the server expects
+      ##                          (but does not guarantee!) that the
+      ##                          application will only be invoked this one
+      ##                          time during the life of its containing
+      ##                          process. Normally, this will only be true
+      ##                          for a server based on CGI
+      ##                          (or something similar).
+
+      ## <tt>rack.hijack?</tt>:: present and true if the server supports
+      ##                         connection hijacking. See below, hijacking.
+
+      ## <tt>rack.hijack</tt>:: an object responding to #call that must be
+      ##                        called at least once before using
+      ##                        rack.hijack_io.
+      ##                        It is recommended #call return rack.hijack_io
+      ##                        as well as setting it in env if necessary.
+
+      ## <tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack
+      ##                           has received #call, this will contain
+      ##                           an object resembling an IO. See hijacking.
 
       ## Additional environment specifications have approved to
       ## standardized middleware APIs.  None of these are required to
       ## be implemented by the server.
 
-      ## <tt>rack.session</tt>:: A hash like interface for storing request session data.
+      ## <tt>rack.session</tt>:: A hash like interface for storing
+      ##                         request session data.
       ##                         The store must implement:
       if session = env['rack.session']
         ##                         store(key, value)         (aliased as []=);
@@ -218,7 +259,6 @@ module Rack
         }
       }
 
-      ##
       ## There are the following restrictions:
 
       ## * <tt>rack.version</tt> must be an array of Integers.
@@ -311,15 +351,23 @@ module Rack
         v
       end
 
-      ## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
-      ##   If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must
-      ##   be a String and may not be nil. If +length+ is given and not nil, then this method
-      ##   reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
-      ##   then this method reads all data until EOF.
-      ##   When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
-      ##   if +length+ is not given or is nil.
-      ##   If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
-      ##   newly created String object.
+      ## * +read+ behaves like IO#read.
+      ##   Its signature is <tt>read([length, [buffer]])</tt>.
+      ##
+      ##   If given, +length+ must be a non-negative Integer (>= 0) or +nil+,
+      ##   and +buffer+ must be a String and may not be nil.
+      ##
+      ##   If +length+ is given and not nil, then this method reads at most
+      ##   +length+ bytes from the input stream.
+      ##
+      ##   If +length+ is not given or nil, then this method reads
+      ##   all data until EOF.
+      ##
+      ##   When EOF is reached, this method returns nil if +length+ is given
+      ##   and not nil, or "" if +length+ is not given or is nil.
+      ##
+      ##   If +buffer+ is given, then the read data will be placed
+      ##   into +buffer+ instead of a newly created String object.
       def read(*args)
         assert("rack.input#read called with too many arguments") {
           args.size <= 2
diff --git a/lib/rack/lobster.rb b/lib/rack/lobster.rb
index d1a7f7bc..195bd945 100644
--- a/lib/rack/lobster.rb
+++ b/lib/rack/lobster.rb
@@ -32,9 +32,14 @@ module Rack
     def call(env)
       req = Request.new(env)
       if req.GET["flip"] == "left"
-        lobster = LobsterString.split("\n").
-          map { |line| line.ljust(42).reverse }.
-          join("\n")
+        lobster = LobsterString.split("\n").map do |line|
+          line.ljust(42).reverse.
+            gsub('\\', 'TEMP').
+            gsub('/', '\\').
+            gsub('TEMP', '/').
+            gsub('{','}').
+            gsub('(',')')
+        end.join("\n")
         href = "?flip=right"
       elsif req.GET["flip"] == "crash"
         raise "Lobster crashed"
diff --git a/lib/rack/methodoverride.rb b/lib/rack/methodoverride.rb
index 449961ce..062f3d67 100644
--- a/lib/rack/methodoverride.rb
+++ b/lib/rack/methodoverride.rb
@@ -4,13 +4,14 @@ module Rack
 
     METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
     HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
+    ALLOWED_METHODS = ["POST"]
 
     def initialize(app)
       @app = app
     end
 
     def call(env)
-      if env["REQUEST_METHOD"] == "POST"
+      if allowed_methods.include?(env["REQUEST_METHOD"])
         method = method_override(env)
         if HTTP_METHODS.include?(method)
           env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
@@ -23,9 +24,19 @@ module Rack
 
     def method_override(env)
       req = Request.new(env)
-      method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
+      method = method_override_param(req) ||
         env[HTTP_METHOD_OVERRIDE_HEADER]
       method.to_s.upcase
     end
+
+    private
+
+    def allowed_methods
+      ALLOWED_METHODS
+    end
+
+    def method_override_param(req)
+      req.POST[METHOD_OVERRIDE_PARAM_KEY]
+    end
   end
 end
diff --git a/lib/rack/mock.rb b/lib/rack/mock.rb
index 3ba314e4..3c02c1fe 100644
--- a/lib/rack/mock.rb
+++ b/lib/rack/mock.rb
@@ -77,9 +77,16 @@ module Rack
       body.close if body.respond_to?(:close)
     end
 
+    # For historical reasons, we're pinning to RFC 2396. It's easier for users
+    # and we get support from ruby 1.8 to 2.2 using this method.
+    def self.parse_uri_rfc2396(uri)
+      @parser ||= defined?(URI::RFC2396_Parser) ? URI::RFC2396_Parser.new : URI
+      @parser.parse(uri)
+    end
+
     # Return the Rack environment used for a request to +uri+.
     def self.env_for(uri="", opts={})
-      uri = URI(uri)
+      uri = parse_uri_rfc2396(uri)
       uri.path = "/#{uri.path}" unless uri.path[0] == ?/
 
       env = DEFAULT_ENV.dup
diff --git a/lib/rack/multipart.rb b/lib/rack/multipart.rb
index d67ff051..7a44c4d4 100644
--- a/lib/rack/multipart.rb
+++ b/lib/rack/multipart.rb
@@ -9,7 +9,7 @@ module Rack
 
     EOL = "\r\n"
     MULTIPART_BOUNDARY = "AaB03x"
-    MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
+    MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|ni
     TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
     CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
     DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})/
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb
index fa47fd16..22f9734b 100644
--- a/lib/rack/multipart/parser.rb
+++ b/lib/rack/multipart/parser.rb
@@ -16,10 +16,10 @@ module Rack
         content_length = env['CONTENT_LENGTH']
         content_length = content_length.to_i if content_length
 
-        new($1, io, content_length)
+        new($1, io, content_length, env)
       end
 
-      def initialize(boundary, io, content_length)
+      def initialize(boundary, io, content_length, env)
         @buf            = ""
 
         if @buf.respond_to? :force_encoding
@@ -31,6 +31,7 @@ module Rack
         @io             = io
         @content_length = content_length
         @boundary_size  = Utils.bytesize(@boundary) + EOL.size
+        @env = env
 
         if @content_length
           @content_length -= @boundary_size
@@ -111,8 +112,12 @@ module Rack
 
             filename = get_filename(head)
 
+            if name.nil? || name.empty? && filename
+              name = filename
+            end
+
             if filename
-              body = Tempfile.new("RackMultipart")
+              (@env['rack.tempfiles'] ||= []) << body = Tempfile.new("RackMultipart")
               body.binmode  if body.respond_to?(:binmode)
             end
 
diff --git a/lib/rack/multipart/uploaded_file.rb b/lib/rack/multipart/uploaded_file.rb
index 11932b17..1b56ad75 100644
--- a/lib/rack/multipart/uploaded_file.rb
+++ b/lib/rack/multipart/uploaded_file.rb
@@ -11,7 +11,7 @@ module Rack
         raise "#{path} file does not exist" unless ::File.exist?(path)
         @content_type = content_type
         @original_filename = ::File.basename(path)
-        @tempfile = Tempfile.new(@original_filename)
+        @tempfile = Tempfile.new([@original_filename, ::File.extname(path)])
         @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
         @tempfile.binmode if binary
         FileUtils.copy_file(path, @tempfile.path)
@@ -31,4 +31,4 @@ module Rack
       end
     end
   end
-end \ No newline at end of file
+end
diff --git a/lib/rack/request.rb b/lib/rack/request.rb
index 80d5e0db..52ea652c 100644
--- a/lib/rack/request.rb
+++ b/lib/rack/request.rb
@@ -8,10 +8,6 @@ module Rack
   #   req = Rack::Request.new(env)
   #   req.post?
   #   req.params["data"]
-  #
-  # The environment hash passed will store a reference to the Request object
-  # instantiated so that it will only instantiate if an instance of the Request
-  # object doesn't already exist.
 
   class Request
     # The environment of the request.
@@ -56,7 +52,7 @@ module Rack
       return {} if content_type.nil?
       Hash[*content_type.split(/\s*[;,]\s*/)[1..-1].
         collect { |s| s.split('=', 2) }.
-        map { |k,v| [k.downcase, v] }.flatten]
+        map { |k,v| [k.downcase, strip_doublequotes(v)] }.flatten]
     end
 
     # The character set of the request body if a "charset" media type
@@ -142,7 +138,7 @@ module Rack
 
     # Checks the HTTP request method (or verb) to see if it was of type TRACE
     def trace?;   request_method == "TRACE"   end
-    
+
     # Checks the HTTP request method (or verb) to see if it was of type UNLINK
     def unlink?;  request_method == "UNLINK"  end
 
@@ -192,8 +188,9 @@ module Rack
       if @env["rack.request.query_string"] == query_string
         @env["rack.request.query_hash"]
       else
+        p = parse_query(query_string)
         @env["rack.request.query_string"] = query_string
-        @env["rack.request.query_hash"]   = parse_query(query_string)
+        @env["rack.request.query_hash"]   = p
       end
     end
 
@@ -337,14 +334,11 @@ module Rack
     end
 
     def accept_encoding
-      @env["HTTP_ACCEPT_ENCODING"].to_s.split(/\s*,\s*/).map do |part|
-        encoding, parameters = part.split(/\s*;\s*/, 2)
-        quality = 1.0
-        if parameters and /\Aq=([\d.]+)/ =~ parameters
-          quality = $1.to_f
-        end
-        [encoding, quality]
-      end
+      parse_http_accept_header(@env["HTTP_ACCEPT_ENCODING"])
+    end
+
+    def accept_language
+      parse_http_accept_header(@env["HTTP_ACCEPT_LANGUAGE"])
     end
 
     def trusted_proxy?(ip)
@@ -359,12 +353,6 @@ module Rack
 
       forwarded_ips = split_ip_addresses(@env['HTTP_X_FORWARDED_FOR'])
 
-      if client_ip = @env['HTTP_CLIENT_IP']
-        # If forwarded_ips doesn't include the client_ip, it might be an
-        # ip spoofing attempt, so we ignore HTTP_CLIENT_IP
-        return client_ip if forwarded_ips.include?(client_ip)
-      end
-
       return reject_trusted_ip_addresses(forwarded_ips).last || @env["REMOTE_ADDR"]
     end
 
@@ -378,11 +366,31 @@ module Rack
       end
 
       def parse_query(qs)
-        Utils.parse_nested_query(qs)
+        Utils.parse_nested_query(qs, '&')
       end
 
       def parse_multipart(env)
         Rack::Multipart.parse_multipart(env)
       end
+
+      def parse_http_accept_header(header)
+        header.to_s.split(/\s*,\s*/).map do |part|
+          attribute, parameters = part.split(/\s*;\s*/, 2)
+          quality = 1.0
+          if parameters and /\Aq=([\d.]+)/ =~ parameters
+            quality = $1.to_f
+          end
+          [attribute, quality]
+        end
+      end
+
+  private
+    def strip_doublequotes(s)
+      if s[0] == ?" && s[-1] == ?"
+        s[1..-2]
+      else
+        s
+      end
+    end
   end
 end
diff --git a/lib/rack/response.rb b/lib/rack/response.rb
index 2076aff0..12536710 100644
--- a/lib/rack/response.rb
+++ b/lib/rack/response.rb
@@ -1,5 +1,6 @@
 require 'rack/request'
 require 'rack/utils'
+require 'rack/body_proxy'
 require 'time'
 
 module Rack
@@ -121,11 +122,14 @@ module Rack
       def server_error?;       status >= 500 && status < 600;        end
 
       def ok?;                 status == 200;                        end
+      def created?;            status == 201;                        end
+      def accepted?;           status == 202;                        end
       def bad_request?;        status == 400;                        end
       def unauthorized?;       status == 401;                        end
       def forbidden?;          status == 403;                        end
       def not_found?;          status == 404;                        end
       def method_not_allowed?; status == 405;                        end
+      def i_m_a_teapot?;       status == 418;                        end
       def unprocessable?;      status == 422;                        end
 
       def redirect?;           [301, 302, 303, 307].include? status; end
diff --git a/lib/rack/sendfile.rb b/lib/rack/sendfile.rb
index 8a674904..8e2c3d67 100644
--- a/lib/rack/sendfile.rb
+++ b/lib/rack/sendfile.rb
@@ -1,4 +1,5 @@
 require 'rack/file'
+require 'rack/body_proxy'
 
 module Rack
 
@@ -117,8 +118,10 @@ module Rack
           if url = map_accel_path(env, path)
             headers['Content-Length'] = '0'
             headers[type] = url
-            body.close if body.respond_to?(:close)
-            body = []
+            obody = body
+            body = Rack::BodyProxy.new([]) do
+              obody.close if obody.respond_to?(:close)
+            end
           else
             env['rack.errors'].puts "X-Accel-Mapping header missing"
           end
@@ -126,8 +129,10 @@ module Rack
           path = F.expand_path(body.to_path)
           headers['Content-Length'] = '0'
           headers[type] = path
-          body.close if body.respond_to?(:close)
-          body = []
+          obody = body
+          body = Rack::BodyProxy.new([]) do
+            obody.close if obody.respond_to?(:close)
+          end
         when '', nil
         else
           env['rack.errors'].puts "Unknown x-sendfile variation: '#{type}'.\n"
diff --git a/lib/rack/server.rb b/lib/rack/server.rb
index be7014c6..d2f0b954 100644
--- a/lib/rack/server.rb
+++ b/lib/rack/server.rb
@@ -1,7 +1,10 @@
 require 'optparse'
 
+
 module Rack
+
   class Server
+
     class Options
       def parse!(args)
         options = {}
@@ -166,7 +169,7 @@ module Rack
     # * :Port
     #     the port to bind to (used by supporting Rack::Handler)
     # * :AccessLog
-    #     webrick acess log options (or supporting Rack::Handler)
+    #     webrick access log options (or supporting Rack::Handler)
     # * :debug
     #     turn on debug output ($DEBUG = true)
     # * :warn
@@ -202,29 +205,44 @@ module Rack
       @app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
     end
 
-    def self.logging_middleware
-      lambda { |server|
-        server.server.name =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr]
-      }
-    end
+    class << self
+      def logging_middleware
+        lambda { |server|
+          server.server.name =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr]
+        }
+      end
 
-    def self.middleware
-      @middleware ||= begin
+      def default_middleware_by_environment
         m = Hash.new {|h,k| h[k] = []}
-        m["deployment"].concat [
+        m["deployment"] = [
           [Rack::ContentLength],
           [Rack::Chunked],
-          logging_middleware
+          logging_middleware,
+          [Rack::TempfileReaper]
         ]
-        m["development"].concat m["deployment"] + [[Rack::ShowExceptions], [Rack::Lint]]
+        m["development"] = [
+          [Rack::ContentLength],
+          [Rack::Chunked],
+          logging_middleware,
+          [Rack::ShowExceptions],
+          [Rack::Lint],
+          [Rack::TempfileReaper]
+        ]
+
         m
       end
+
+      # Aliased for backwards-compatibility
+      alias :middleware :default_middleware_by_environment
     end
 
-    def middleware
-      self.class.middleware
+    def default_middleware_by_environment
+      self.class.default_middleware_by_environment
     end
 
+    # Aliased for backwards-compatibility
+    alias :middleware :default_middleware_by_environment
+
     def start &blk
       if options[:warn]
         $-w = true
@@ -304,7 +322,8 @@ module Rack
       end
 
       def build_app(app)
-        middleware[options[:environment]].reverse_each do |middleware|
+        middlewares = default_middleware_by_environment[options[:environment]]
+        middlewares.reverse_each do |middleware|
           middleware = middleware.call(self) if middleware.respond_to?(:call)
           next unless middleware
           klass, *args = middleware
@@ -364,4 +383,5 @@ module Rack
       end
 
   end
+
 end
diff --git a/lib/rack/session/memcache.rb b/lib/rack/session/memcache.rb
index 53261fda..c0e1f3ec 100644
--- a/lib/rack/session/memcache.rb
+++ b/lib/rack/session/memcache.rb
@@ -47,7 +47,7 @@ module Rack
       end
 
       def get_session(env, sid)
-        with_lock(env, [nil, {}]) do
+        with_lock(env) do
           unless sid and session = @pool.get(sid)
             sid, session = generate_sid, {}
             unless /^STORED/ =~ @pool.add(sid, session)
@@ -62,7 +62,7 @@ module Rack
         expiry = options[:expire_after]
         expiry = expiry.nil? ? 0 : expiry + 1
 
-        with_lock(env, false) do
+        with_lock(env) do
           @pool.set session_id, new_session, expiry
           session_id
         end
@@ -75,7 +75,7 @@ module Rack
         end
       end
 
-      def with_lock(env, default=nil)
+      def with_lock(env)
         @mutex.lock if env['rack.multithread']
         yield
       rescue MemCache::MemCacheError, Errno::ECONNREFUSED
@@ -83,7 +83,7 @@ module Rack
           warn "#{self} is unable to find memcached server."
           warn $!.inspect
         end
-        default
+        raise
       ensure
         @mutex.unlock if @mutex.locked?
       end
diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb
index d4774fed..fcb34ec4 100644
--- a/lib/rack/session/pool.rb
+++ b/lib/rack/session/pool.rb
@@ -42,7 +42,7 @@ module Rack
       end
 
       def get_session(env, sid)
-        with_lock(env, [nil, {}]) do
+        with_lock(env) do
           unless sid and session = @pool[sid]
             sid, session = generate_sid, {}
             @pool.store sid, session
@@ -52,7 +52,7 @@ module Rack
       end
 
       def set_session(env, session_id, new_session, options)
-        with_lock(env, false) do
+        with_lock(env) do
           @pool.store session_id, new_session
           session_id
         end
@@ -65,15 +65,12 @@ module Rack
         end
       end
 
-      def with_lock(env, default=nil)
+      def with_lock(env)
         @mutex.lock if env['rack.multithread']
         yield
-      rescue
-        default
       ensure
         @mutex.unlock if @mutex.locked?
       end
-
     end
   end
 end
diff --git a/lib/rack/showexceptions.rb b/lib/rack/showexceptions.rb
index c91ca07c..731aea49 100644
--- a/lib/rack/showexceptions.rb
+++ b/lib/rack/showexceptions.rb
@@ -28,23 +28,32 @@ module Rack
       env["rack.errors"].puts(exception_string)
       env["rack.errors"].flush
 
-      if prefers_plain_text?(env)
-        content_type = "text/plain"
-        body = [exception_string]
-      else
+      if accepts_html?(env)
         content_type = "text/html"
         body = pretty(env, e)
+      else
+        content_type = "text/plain"
+        body = exception_string
       end
 
-      [500,
-       {"Content-Type" => content_type,
-        "Content-Length" => Rack::Utils.bytesize(body.join).to_s},
-       body]
+      [
+        500,
+        {
+          "Content-Type" => content_type,
+          "Content-Length" => Rack::Utils.bytesize(body).to_s,
+        },
+        [body],
+      ]
+    end
+
+    def prefers_plaintext?(env)
+      !accepts_html(env)
     end
 
-    def prefers_plain_text?(env)
-      env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" && (!env["HTTP_ACCEPT"] || !env["HTTP_ACCEPT"].include?("text/html"))
+    def accepts_html?(env)
+      Rack::Utils.best_q_match(env["HTTP_ACCEPT"], %w[text/html])
     end
+    private :accepts_html?
 
     def dump_exception(exception)
       string = "#{exception.class}: #{exception.message}\n"
@@ -85,7 +94,7 @@ module Rack
         end
       }.compact
 
-      [@template.result(binding)]
+      @template.result(binding)
     end
 
     def h(obj)                  # :nodoc:
diff --git a/lib/rack/tempfile_reaper.rb b/lib/rack/tempfile_reaper.rb
new file mode 100644
index 00000000..1500b06a
--- /dev/null
+++ b/lib/rack/tempfile_reaper.rb
@@ -0,0 +1,22 @@
+require 'rack/body_proxy'
+
+module Rack
+
+  # Middleware tracks and cleans Tempfiles created throughout a request (i.e. Rack::Multipart)
+  # Ideas/strategy based on posts by Eric Wong and Charles Oliver Nutter
+  # https://groups.google.com/forum/#!searchin/rack-devel/temp/rack-devel/brK8eh-MByw/sw61oJJCGRMJ
+  class TempfileReaper
+    def initialize(app)
+      @app = app
+    end
+
+    def call(env)
+      env['rack.tempfiles'] ||= []
+      status, headers, body = @app.call(env)
+      body_proxy = BodyProxy.new(body) do
+        env['rack.tempfiles'].each { |f| f.close! } unless env['rack.tempfiles'].nil?
+      end
+      [status, headers, body_proxy]
+    end
+  end
+end
diff --git a/lib/rack/urlmap.rb b/lib/rack/urlmap.rb
index d301ce9b..df9e7d6d 100644
--- a/lib/rack/urlmap.rb
+++ b/lib/rack/urlmap.rb
@@ -48,9 +48,10 @@ module Rack
       sPort = env['SERVER_PORT']
 
       @mapping.each do |host, location, match, app|
-        unless hHost == host \
-            || sName == host \
-            || (!host && (hHost == sName || hHost == sName+':'+sPort))
+        unless casecmp?(hHost, host) \
+            || casecmp?(sName, host) \
+            || (!host && (casecmp?(hHost, sName) ||
+                          casecmp?(hHost, sName+':'+sPort)))
           next
         end
 
@@ -71,6 +72,19 @@ module Rack
       env['PATH_INFO'] = path
       env['SCRIPT_NAME'] = script_name
     end
+
+    private
+    def casecmp?(v1, v2)
+      # if both nil, or they're the same string
+      return true if v1 == v2
+
+      # if either are nil... (but they're not the same)
+      return false if v1.nil?
+      return false if v2.nil?
+
+      # otherwise check they're not case-insensitive the same
+      v1.casecmp(v2).zero?
+    end
   end
 end
 
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index fa3c0df8..53303995 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -22,6 +22,15 @@ module Rack
   # applications adopted from all kinds of Ruby libraries.
 
   module Utils
+    # ParameterTypeError is the error that is raised when incoming structural
+    # parameters (parsed by parse_nested_query) contain conflicting types.
+    class ParameterTypeError < TypeError; end
+
+    # InvalidParameterError is the error that is raised when incoming structural
+    # parameters (parsed by parse_nested_query) contain invalid format or byte
+    # sequence.
+    class InvalidParameterError < ArgumentError; end
+
     # URI escapes. (CGI style space to +)
     def escape(s)
       URI.encode_www_form_component(s)
@@ -87,6 +96,11 @@ module Rack
     end
     module_function :parse_query
 
+    # parse_nested_query expands a query string into structural types. Supported
+    # types are Arrays, Hashes and basic value types. It is possible to supply
+    # query strings with parameters of conflicting types, in this case a
+    # ParameterTypeError is raised. Users are encouraged to return a 400 in this
+    # case.
     def parse_nested_query(qs, d = nil)
       params = KeySpaceConstrainedParams.new
 
@@ -97,9 +111,14 @@ module Rack
       end
 
       return params.to_params_hash
+    rescue ArgumentError => e
+      raise InvalidParameterError, e.message
     end
     module_function :parse_nested_query
 
+    # normalize_params recursively expands parameters into structural types. If
+    # the structural types represented by two different parameter names are in
+    # conflict, a ParameterTypeError is raised.
     def normalize_params(params, name, v = nil)
       name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
       k = $1 || ''
@@ -109,14 +128,16 @@ module Rack
 
       if after == ""
         params[k] = v
+      elsif after == "["
+        params[name] = v
       elsif after == "[]"
         params[k] ||= []
-        raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
+        raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
         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)
+        raise ParameterTypeError, "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)
         else
@@ -124,7 +145,7 @@ module Rack
         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])
+        raise ParameterTypeError, "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
 
@@ -182,13 +203,14 @@ module Rack
     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) }
+      matches = values.map do |req_mime, quality|
+        match = available_mimes.find { |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.last
+      matches && matches.first
     end
     module_function :best_q_match
 
@@ -530,7 +552,11 @@ module Rack
         hash.keys.each do |key|
           value = hash[key]
           if value.kind_of?(self.class)
-            hash[key] = value.to_params_hash
+            if value.object_id == self.object_id
+              hash[key] = hash
+            else
+              hash[key] = value.to_params_hash
+            end
           elsif value.kind_of?(Array)
             value.map! {|x| x.kind_of?(self.class) ? x.to_params_hash : x}
           end
@@ -585,6 +611,7 @@ module Rack
       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',
@@ -609,7 +636,7 @@ module Rack
     STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 205 << 304)
 
     SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
-      [message.downcase.gsub(/\s|-/, '_').to_sym, code]
+      [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
     }.flatten]
 
     def status_code(status)
@@ -623,5 +650,23 @@ module Rack
 
     Multipart = Rack::Multipart
 
+    PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
+
+    def clean_path_info(path_info)
+      parts = path_info.split PATH_SEPS
+
+      clean = []
+
+      parts.each do |part|
+        next if part.empty? || part == '.'
+        part == '..' ? clean.pop : clean << part
+      end
+
+      clean.unshift '/' if parts.empty? || parts.first.empty?
+
+      ::File.join(*clean)
+    end
+    module_function :clean_path_info
+
   end
 end
diff --git a/test/multipart/filename_and_no_name b/test/multipart/filename_and_no_name
new file mode 100644
index 00000000..00d58153
--- /dev/null
+++ b/test/multipart/filename_and_no_name
@@ -0,0 +1,6 @@
+--AaB03x
+Content-Disposition: form-data; filename="file1.txt"
+Content-Type: text/plain
+
+contents
+--AaB03x--
diff --git a/test/spec_chunked.rb b/test/spec_chunked.rb
index 12f21581..0a6d9ff1 100644
--- a/test/spec_chunked.rb
+++ b/test/spec_chunked.rb
@@ -64,6 +64,22 @@ describe Rack::Chunked do
     body.join.should.equal 'Hello World!'
   end
 
+  should 'not modify response when client is ancient, pre-HTTP/1.0' do
+    app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
+    check = lambda do
+      status, headers, body = chunked(app).call(@env.dup)
+      status.should.equal 200
+      headers.should.not.include 'Transfer-Encoding'
+      body.join.should.equal 'Hello World!'
+    end
+
+    @env.delete('HTTP_VERSION') # unicorn will do this on pre-HTTP/1.0 requests
+    check.call
+
+    @env['HTTP_VERSION'] = 'HTTP/0.9' # not sure if this happens in practice
+    check.call
+  end
+
   should 'not modify response when Transfer-Encoding header already present' do
     app = lambda { |env|
       [200, {"Content-Type" => "text/plain", 'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']]
diff --git a/test/spec_content_length.rb b/test/spec_content_length.rb
index 4b80a0f4..12c047fb 100644
--- a/test/spec_content_length.rb
+++ b/test/spec_content_length.rb
@@ -62,7 +62,9 @@ describe Rack::ContentLength do
     end.new(%w[one two three])
 
     app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
-    content_length(app).call(request)
+    response = content_length(app).call(request)
+    body.closed.should.equal nil
+    response[2].close
     body.closed.should.equal true
   end
 
diff --git a/test/spec_etag.rb b/test/spec_etag.rb
index d7f03504..c075d9d0 100644
--- a/test/spec_etag.rb
+++ b/test/spec_etag.rb
@@ -21,13 +21,13 @@ describe Rack::ETag do
   should "set ETag if none is set if status is 200" do
     app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
     response = etag(app).call(request)
-    response[1]['ETag'].should.equal "\"65a8e27d8879283831b664bd8b7f0ad4\""
+    response[1]['ETag'].should.equal "W/\"65a8e27d8879283831b664bd8b7f0ad4\""
   end
 
   should "set ETag if none is set if status is 201" do
     app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
     response = etag(app).call(request)
-    response[1]['ETag'].should.equal "\"65a8e27d8879283831b664bd8b7f0ad4\""
+    response[1]['ETag'].should.equal "W/\"65a8e27d8879283831b664bd8b7f0ad4\""
   end
 
   should "set Cache-Control to 'max-age=0, private, must-revalidate' (default) if none is set" do
@@ -95,4 +95,13 @@ describe Rack::ETag do
     response = etag(app).call(request)
     response[1]['ETag'].should.be.nil
   end
+
+  should "close the original body" do
+    body = StringIO.new
+    app = lambda { |env| [200, {}, body] }
+    response = etag(app).call(request)
+    body.should.not.be.closed
+    response[2].close
+    body.should.be.closed
+  end
 end
diff --git a/test/spec_head.rb b/test/spec_head.rb
index 18f9a76a..78bc6ad7 100644
--- a/test/spec_head.rb
+++ b/test/spec_head.rb
@@ -38,6 +38,8 @@ describe Rack::Head do
     resp[0].should.equal(200)
     resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
     resp[2].to_enum.to_a.should.equal([])
+    body.should.not.be.closed
+    resp[2].close
     body.should.be.closed
   end
 end
diff --git a/test/spec_lobster.rb b/test/spec_lobster.rb
index 56a54795..c6ec2b06 100644
--- a/test/spec_lobster.rb
+++ b/test/spec_lobster.rb
@@ -47,7 +47,7 @@ describe Rack::Lobster do
   should "be flippable" do
     res = lobster.get("/?flip=left")
     res.should.be.ok
-    res.body.should.include "(,,,(,,(,("
+    res.body.should.include "),,,),,),)"
   end
 
   should "provide crashing for testing purposes" do
diff --git a/test/spec_mock.rb b/test/spec_mock.rb
index f49b1961..3ebd7776 100644
--- a/test/spec_mock.rb
+++ b/test/spec_mock.rb
@@ -30,6 +30,14 @@ describe Rack::MockRequest do
     env.should.include "rack.version"
   end
 
+  should "return an environment with a path" do
+    env = Rack::MockRequest.env_for("http://www.example.com/parse?location[]=1&location[]=2&age_group[]=2")
+    env["QUERY_STRING"].should.equal "location[]=1&location[]=2&age_group[]=2"
+    env["PATH_INFO"].should.equal "/parse"
+    env.should.be.kind_of Hash
+    env.should.include "rack.version"
+  end
+
   should "provide sensible defaults" do
     res = Rack::MockRequest.new(app).request
 
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index 069dc4d2..2acb6e0d 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -153,6 +153,18 @@ describe Rack::Multipart do
     params["files"][:tempfile].read.should.equal "contents"
   end
 
+  should "parse multipart upload with text file with no name field" do
+    env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_no_name))
+    params = Rack::Multipart.parse_multipart(env)
+    params["file1.txt"][:type].should.equal "text/plain"
+    params["file1.txt"][:filename].should.equal "file1.txt"
+    params["file1.txt"][:head].should.equal "Content-Disposition: form-data; " +
+      "filename=\"file1.txt\"\r\n" +
+      "Content-Type: text/plain\r\n"
+    params["file1.txt"][:name].should.equal "file1.txt"
+    params["file1.txt"][:tempfile].read.should.equal "contents"
+  end
+
   should "parse multipart upload with nested parameters" do
     env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
     params = Rack::Multipart.parse_multipart(env)
@@ -502,4 +514,28 @@ contents\r
     params["file"][:filename].should.equal('long' * 100)
   end
 
+  should "support mixed case metadata" do
+    file = multipart_file(:text)
+    data = File.open(file, 'rb') { |io| io.read }
+
+    type = "Multipart/Form-Data; Boundary=AaB03x"
+    length = data.respond_to?(:bytesize) ? data.bytesize : data.size
+
+    e = { "CONTENT_TYPE" => type,
+      "CONTENT_LENGTH" => length.to_s,
+      :input => StringIO.new(data) }
+
+    env = Rack::MockRequest.env_for("/", e)
+    params = Rack::Multipart.parse_multipart(env)
+    params["submit-name"].should.equal "Larry"
+    params["submit-name-with-content"].should.equal "Berry"
+    params["files"][:type].should.equal "text/plain"
+    params["files"][:filename].should.equal "file1.txt"
+    params["files"][:head].should.equal "Content-Disposition: form-data; " +
+      "name=\"files\"; filename=\"file1.txt\"\r\n" +
+      "Content-Type: text/plain\r\n"
+    params["files"][:name].should.equal "files"
+    params["files"][:tempfile].read.should.equal "contents"
+  end
+
 end
diff --git a/test/spec_request.rb b/test/spec_request.rb
index a3f42379..8a2b4760 100644
--- a/test/spec_request.rb
+++ b/test/spec_request.rb
@@ -130,6 +130,14 @@ describe Rack::Request do
     req.params.should.equal "foo" => "bar", "quux" => "bla"
   end
 
+  should "not truncate query strings containing semi-colons #543" do
+    req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=b;la"))
+    req.query_string.should.equal "foo=bar&quux=b;la"
+    req.GET.should.equal "foo" => "bar", "quux" => "b;la"
+    req.POST.should.be.empty
+    req.params.should.equal "foo" => "bar", "quux" => "b;la"
+  end
+
   should "limit the keys from the GET query string" do
     env = Rack::MockRequest.env_for("/?foo=bar")
 
@@ -143,7 +151,7 @@ describe Rack::Request do
   end
 
   should "limit the key size per nested params hash" do
-    nested_query = Rack::MockRequest.env_for("/?foo[bar][baz][qux]=1")
+    nested_query = Rack::MockRequest.env_for("/?foo%5Bbar%5D%5Bbaz%5D%5Bqux%5D=1")
     plain_query  = Rack::MockRequest.env_for("/?foo_bar__baz__qux_=1")
 
     old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3
@@ -169,6 +177,18 @@ describe Rack::Request do
     req.params.should.equal req.GET.merge(req.POST)
   end
 
+  should "raise if input params has invalid %-encoding" do
+    mr = Rack::MockRequest.env_for("/?foo=quux",
+      "REQUEST_METHOD" => 'POST',
+      :input => "a%=1"
+    )
+    req = Rack::Request.new mr
+
+    lambda { req.POST }.
+      should.raise(Rack::Utils::InvalidParameterError).
+      message.should.equal "invalid %-encoding (a%)"
+  end
+
   should "raise if rack.input is missing" do
     req = Rack::Request.new({})
     lambda { req.POST }.should.raise(RuntimeError)
@@ -603,7 +623,7 @@ describe Rack::Request do
   should "handle multiple media type parameters" do
     req = Rack::Request.new \
       Rack::MockRequest.env_for("/",
-        "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam')
+        "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam;blong="boo";zump="zoo\"o";weird=lol"')
       req.should.not.be.form_data
       req.media_type_params.should.include 'foo'
       req.media_type_params['foo'].should.equal 'BAR'
@@ -612,6 +632,9 @@ describe Rack::Request do
       req.media_type_params.should.not.include 'BLING'
       req.media_type_params.should.include 'bling'
       req.media_type_params['bling'].should.equal 'bam'
+      req.media_type_params['blong'].should.equal 'boo'
+      req.media_type_params['zump'].should.equal 'zoo\"o'
+      req.media_type_params['weird'].should.equal 'lol"'
   end
 
   should "parse with junk before boundry" do
@@ -740,6 +763,31 @@ EOF
     req.POST["mean"][:tempfile].read.should.equal "--AaB03xha"
   end
 
+  should "record tempfiles from multipart form data in env[rack.tempfiles]" do
+    input = <<EOF
+--AaB03x\r
+content-disposition: form-data; name="fileupload"; filename="foo.jpg"\r
+Content-Type: image/jpeg\r
+Content-Transfer-Encoding: base64\r
+\r
+/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
+--AaB03x\r
+content-disposition: form-data; name="fileupload"; filename="bar.jpg"\r
+Content-Type: image/jpeg\r
+Content-Transfer-Encoding: base64\r
+\r
+/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
+--AaB03x--\r
+EOF
+    env = Rack::MockRequest.env_for("/",
+                          "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
+                          "CONTENT_LENGTH" => input.size,
+                          :input => input)
+    req = Rack::Request.new(env)
+    req.params
+    env['rack.tempfiles'].size.should.equal(2)
+  end
+
   should "detect invalid multipart form data" do
     input = <<EOF
 --AaB03x\r
@@ -938,6 +986,23 @@ EOF
     parser.call("gzip ; deflate").should.equal([["gzip", 1.0]])
   end
 
+  should "parse Accept-Language correctly" do
+    parser = lambda do |x|
+      Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_LANGUAGE" => x)).accept_language
+    end
+
+    parser.call(nil).should.equal([])
+
+    parser.call("fr, en").should.equal([["fr", 1.0], ["en", 1.0]])
+    parser.call("").should.equal([])
+    parser.call("*").should.equal([["*", 1.0]])
+    parser.call("fr;q=0.5, en;q=1.0").should.equal([["fr", 0.5], ["en", 1.0]])
+    parser.call("fr;q=1.0, en; q=0.5, *;q=0").should.equal([["fr", 1.0], ["en", 0.5], ["*", 0] ])
+
+    parser.call("fr ; q=0.9").should.equal([["fr", 0.9]])
+    parser.call("fr").should.equal([["fr", 1.0]])
+  end
+
   ip_app = lambda { |env|
     request = Rack::Request.new(env)
     response = Rack::Response.new
@@ -1020,12 +1085,6 @@ EOF
       'HTTP_CLIENT_IP' => '1.1.1.1'
     res.body.should.equal '1.1.1.1'
 
-    # Spoofing attempt
-    res = mock.get '/',
-      'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
-      'HTTP_CLIENT_IP' => '2.2.2.2'
-    res.body.should.equal '1.1.1.1'
-
     res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9'
     res.body.should.equal '9.9.9.9'
 
@@ -1044,6 +1103,24 @@ EOF
     res.body.should.equal '3.4.5.6'
   end
 
+  should "not allow IP spoofing via Client-IP and X-Forwarded-For headers" do
+    mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
+
+    # IP Spoofing attempt:
+    # Client sends          X-Forwarded-For: 6.6.6.6
+    #                       Client-IP: 6.6.6.6
+    # Load balancer adds    X-Forwarded-For: 2.2.2.3, 192.168.0.7
+    # App receives:         X-Forwarded-For: 6.6.6.6
+    #                       X-Forwarded-For: 2.2.2.3, 192.168.0.7
+    #                       Client-IP: 6.6.6.6
+    # Rack env:             HTTP_X_FORWARDED_FOR: '6.6.6.6, 2.2.2.3, 192.168.0.7'
+    #                       HTTP_CLIENT_IP: '6.6.6.6'
+    res = mock.get '/',
+      'HTTP_X_FORWARDED_FOR' => '6.6.6.6, 2.2.2.3, 192.168.0.7',
+      'HTTP_CLIENT_IP' => '6.6.6.6'
+    res.body.should.equal '2.2.2.3'
+  end
+
   should "regard local addresses as proxies" do
     req = Rack::Request.new(Rack::MockRequest.env_for("/"))
     req.trusted_proxy?('127.0.0.1').should.equal 0
@@ -1098,6 +1175,13 @@ EOF
     req2.params.should.equal "foo" => "bar"
   end
 
+  should "raise TypeError every time if request parameters are broken" do
+    broken_query = Rack::MockRequest.env_for("/?foo%5B%5D=0&foo%5Bbar%5D=1")
+    req = Rack::Request.new(broken_query)
+    lambda{req.GET}.should.raise(TypeError)
+    lambda{req.params}.should.raise(TypeError)
+  end
+
   (0x20...0x7E).collect { |a|
     b = a.chr
     c = CGI.escape(b)
diff --git a/test/spec_response.rb b/test/spec_response.rb
index 12b8b7b3..6b13c0c9 100644
--- a/test/spec_response.rb
+++ b/test/spec_response.rb
@@ -223,6 +223,14 @@ describe Rack::Response do
     res.should.be.successful
     res.should.be.ok
 
+    res.status = 201
+    res.should.be.successful
+    res.should.be.created
+
+    res.status = 202
+    res.should.be.successful
+    res.should.be.accepted
+
     res.status = 400
     res.should.not.be.successful
     res.should.be.client_error
@@ -243,6 +251,11 @@ describe Rack::Response do
     res.should.be.client_error
     res.should.be.method_not_allowed
 
+    res.status = 418
+    res.should.not.be.successful
+    res.should.be.client_error
+    res.should.be.i_m_a_teapot
+
     res.status = 422
     res.should.not.be.successful
     res.should.be.client_error
diff --git a/test/spec_server.rb b/test/spec_server.rb
index 44d4bcbb..01b4f562 100644
--- a/test/spec_server.rb
+++ b/test/spec_server.rb
@@ -30,14 +30,24 @@ describe Rack::Server do
 
   should "not include Rack::Lint in deployment or none environments" do
     server = Rack::Server.new(:app => 'foo')
-    server.middleware['deployment'].flatten.should.not.include(Rack::Lint)
-    server.middleware['none'].flatten.should.not.include(Rack::Lint)
+    server.default_middleware_by_environment['deployment'].flatten.should.not.include(Rack::Lint)
+    server.default_middleware_by_environment['none'].flatten.should.not.include(Rack::Lint)
   end
 
   should "not include Rack::ShowExceptions in deployment or none environments" do
     server = Rack::Server.new(:app => 'foo')
-    server.middleware['deployment'].flatten.should.not.include(Rack::ShowExceptions)
-    server.middleware['none'].flatten.should.not.include(Rack::ShowExceptions)
+    server.default_middleware_by_environment['deployment'].flatten.should.not.include(Rack::ShowExceptions)
+    server.default_middleware_by_environment['none'].flatten.should.not.include(Rack::ShowExceptions)
+  end
+
+  should "always return an empty array for unknown environments" do
+    server = Rack::Server.new(:app => 'foo')
+    server.default_middleware_by_environment['production'].should.equal []
+  end
+
+  should "include Rack::TempfileReaper in deployment environment" do
+    server = Rack::Server.new(:app => 'foo')
+    server.middleware['deployment'].flatten.should.include(Rack::TempfileReaper)
   end
 
   should "support CGI" do
@@ -53,7 +63,7 @@ describe Rack::Server do
 
   should "not force any middleware under the none configuration" do
     server = Rack::Server.new(:app => 'foo')
-    server.middleware['none'].should.be.empty
+    server.default_middleware_by_environment['none'].should.be.empty
   end
 
   should "use a full path to the pidfile" do
diff --git a/test/spec_showexceptions.rb b/test/spec_showexceptions.rb
index bdd5ce5b..7d50c59f 100644
--- a/test/spec_showexceptions.rb
+++ b/test/spec_showexceptions.rb
@@ -16,7 +16,7 @@ describe Rack::ShowExceptions do
     ))
 
     lambda{
-      res = req.get("/")
+      res = req.get("/", "HTTP_ACCEPT" => "text/html")
     }.should.not.raise
 
     res.should.be.a.server_error
@@ -26,7 +26,7 @@ describe Rack::ShowExceptions do
     res.should =~ /ShowExceptions/
   end
 
-  it "responds with plain text on AJAX requests accepting anything but HTML" do
+  it "responds with HTML only to requests accepting HTML" do
     res = nil
 
     req = Rack::MockRequest.new(
@@ -34,39 +34,32 @@ describe Rack::ShowExceptions do
         lambda{|env| raise RuntimeError, "It was never supposed to work" }
     ))
 
-    lambda{
-      res = req.get("/", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
-    }.should.not.raise
-
-    res.should.be.a.server_error
-    res.status.should.equal 500
-
-    res.content_type.should.equal "text/plain"
-
-    res.body.should.include "RuntimeError: It was never supposed to work\n"
-    res.body.should.include __FILE__
-  end
-
-  it "responds with HTML on AJAX requests accepting HTML" do
-    res = nil
-
-    req = Rack::MockRequest.new(
-      show_exceptions(
-        lambda{|env| raise RuntimeError, "It was never supposed to work" }
-    ))
-
-    lambda{
-      res = req.get("/", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", "HTTP_ACCEPT" => "text/html")
-    }.should.not.raise
-
-    res.should.be.a.server_error
-    res.status.should.equal 500
-
-    res.content_type.should.equal "text/html"
-
-    res.body.should.include "RuntimeError"
-    res.body.should.include "It was never supposed to work"
-    res.body.should.include Rack::Utils.escape_html(__FILE__)
+    [
+      # Serve text/html when the client accepts text/html
+      ["text/html", ["/", {"HTTP_ACCEPT" => "text/html"}]],
+      ["text/html", ["/", {"HTTP_ACCEPT" => "*/*"}]],
+      # Serve text/plain when the client does not accept text/html
+      ["text/plain", ["/"]],
+      ["text/plain", ["/", {"HTTP_ACCEPT" => "application/json"}]]
+    ].each do |exmime, rargs|
+      lambda{
+        res = req.get(*rargs)
+      }.should.not.raise
+
+      res.should.be.a.server_error
+      res.status.should.equal 500
+
+      res.content_type.should.equal exmime
+
+      res.body.should.include "RuntimeError"
+      res.body.should.include "It was never supposed to work"
+
+      if exmime == "text/html"
+        res.body.should.include '</html>'
+      else
+        res.body.should.not.include '</html>'
+      end
+    end
   end
 
   it "handles exceptions without a backtrace" do
@@ -79,7 +72,7 @@ describe Rack::ShowExceptions do
     )
 
     lambda{
-      res = req.get("/")
+      res = req.get("/", "HTTP_ACCEPT" => "text/html")
     }.should.not.raise
 
     res.should.be.a.server_error
diff --git a/test/spec_tempfile_reaper.rb b/test/spec_tempfile_reaper.rb
new file mode 100644
index 00000000..ac39d878
--- /dev/null
+++ b/test/spec_tempfile_reaper.rb
@@ -0,0 +1,63 @@
+require 'rack/tempfile_reaper'
+require 'rack/lint'
+require 'rack/mock'
+
+describe Rack::TempfileReaper do
+  class MockTempfile
+    attr_reader :closed
+
+    def initialize
+      @closed = false
+    end
+
+    def close!
+      @closed = true
+    end
+  end
+
+  before do
+    @env = Rack::MockRequest.env_for
+  end
+
+  def call(app)
+    Rack::Lint.new(Rack::TempfileReaper.new(app)).call(@env)
+  end
+
+  should 'do nothing (i.e. not bomb out) without env[rack.tempfiles]' do
+    app = lambda { |_| [200, {}, ['Hello, World!']] }
+    response = call(app)
+    response[2].close
+    response[0].should.equal(200)
+  end
+
+  should 'close env[rack.tempfiles] when body is closed' do
+    tempfile1, tempfile2 = MockTempfile.new, MockTempfile.new
+    @env['rack.tempfiles'] = [ tempfile1, tempfile2 ]
+    app = lambda { |_| [200, {}, ['Hello, World!']] }
+    call(app)[2].close
+    tempfile1.closed.should.equal true
+    tempfile2.closed.should.equal true
+  end
+
+  should 'initialize env[rack.tempfiles] when not already present' do
+    tempfile = MockTempfile.new
+    app = lambda do |env|
+      env['rack.tempfiles'] << tempfile
+      [200, {}, ['Hello, World!']]
+    end
+    call(app)[2].close
+    tempfile.closed.should.equal true
+  end
+
+  should 'append env[rack.tempfiles] when already present' do
+    tempfile1, tempfile2 = MockTempfile.new, MockTempfile.new
+    @env['rack.tempfiles'] = [ tempfile1 ]
+    app = lambda do |env|
+      env['rack.tempfiles'] << tempfile2
+      [200, {}, ['Hello, World!']]
+    end
+    call(app)[2].close
+    tempfile1.closed.should.equal true
+    tempfile2.closed.should.equal true
+  end
+end
diff --git a/test/spec_urlmap.rb b/test/spec_urlmap.rb
index 316c7254..2ef41cdc 100644
--- a/test/spec_urlmap.rb
+++ b/test/spec_urlmap.rb
@@ -210,4 +210,27 @@ describe Rack::URLMap do
     res["X-PathInfo"].should.equal "/http://example.org/bar"
     res["X-ScriptName"].should.equal ""
   end
+
+  should "not be case sensitive with hosts" do
+    map = Rack::Lint.new(Rack::URLMap.new("http://example.org/" => lambda { |env|
+                             [200,
+                              { "Content-Type" => "text/plain",
+                                "X-Position" => "root",
+                                "X-PathInfo" => env["PATH_INFO"],
+                                "X-ScriptName" => env["SCRIPT_NAME"]
+                              }, [""]]}
+                           ))
+
+    res = Rack::MockRequest.new(map).get("http://example.org/")
+    res.should.be.ok
+    res["X-Position"].should.equal "root"
+    res["X-PathInfo"].should.equal "/"
+    res["X-ScriptName"].should.equal ""
+
+    res = Rack::MockRequest.new(map).get("http://EXAMPLE.ORG/")
+    res.should.be.ok
+    res["X-Position"].should.equal "root"
+    res["X-PathInfo"].should.equal "/"
+    res["X-ScriptName"].should.equal ""
+  end
 end
diff --git a/test/spec_utils.rb b/test/spec_utils.rb
index 622b8ff5..4b989db7 100644
--- a/test/spec_utils.rb
+++ b/test/spec_utils.rb
@@ -123,6 +123,17 @@ describe Rack::Utils do
     Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "bar"
   end
 
+  should "not create infinite loops with cycle structures" do
+    ex = { "foo" => nil }
+    ex["foo"] = ex
+
+    params = Rack::Utils::KeySpaceConstrainedParams.new
+    params['foo'] = params
+    lambda {
+      params.to_params_hash.to_s.should.equal ex.to_s
+    }.should.not.raise
+  end
+
   should "parse nested query strings correctly" do
     Rack::Utils.parse_nested_query("foo").
       should.equal "foo" => nil
@@ -157,6 +168,16 @@ describe Rack::Utils do
       should.equal "foo" => [""]
     Rack::Utils.parse_nested_query("foo[]=bar").
       should.equal "foo" => ["bar"]
+    Rack::Utils.parse_nested_query("foo[]=bar&foo").
+      should.equal "foo" => nil
+    Rack::Utils.parse_nested_query("foo[]=bar&foo[").
+      should.equal "foo" => ["bar"], "foo[" => nil
+    Rack::Utils.parse_nested_query("foo[]=bar&foo[=baz").
+      should.equal "foo" => ["bar"], "foo[" => "baz"
+    Rack::Utils.parse_nested_query("foo[]=bar&foo[]").
+      should.equal "foo" => ["bar", nil]
+    Rack::Utils.parse_nested_query("foo[]=bar&foo[]=").
+      should.equal "foo" => ["bar", ""]
 
     Rack::Utils.parse_nested_query("foo[]=1&foo[]=2").
       should.equal "foo" => ["1", "2"]
@@ -192,16 +213,22 @@ describe Rack::Utils do
       should.equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}
 
     lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }.
-      should.raise(TypeError).
+      should.raise(Rack::Utils::ParameterTypeError).
       message.should.equal "expected Hash (got String) for param `y'"
 
     lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }.
-      should.raise(TypeError).
+      should.raise(Rack::Utils::ParameterTypeError).
       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).
+      should.raise(Rack::Utils::ParameterTypeError).
       message.should.equal "expected Array (got String) for param `y'"
+
+    if RUBY_VERSION.to_f > 1.9
+      lambda { Rack::Utils.parse_nested_query("foo%81E=1") }.
+        should.raise(Rack::Utils::InvalidParameterError).
+        message.should.equal "invalid byte sequence in UTF-8"
+    end
   end
 
   should "build query strings correctly" do
@@ -290,9 +317,15 @@ describe Rack::Utils do
     # Higher quality matches are preferred
     Rack::Utils.best_q_match("text/*;q=0.5,text/plain;q=1.0", %w[text/plain text/html]).should.equal "text/plain"
 
+    # Respect requested content type
+    Rack::Utils.best_q_match("application/json", %w[application/vnd.lotus-1-2-3 application/json]).should.equal "application/json"
+
     # All else equal, the available mimes are preferred in order
     Rack::Utils.best_q_match("text/*", %w[text/html text/plain]).should.equal "text/html"
     Rack::Utils.best_q_match("text/plain,text/html", %w[text/html text/plain]).should.equal "text/html"
+
+    # When there are no matches, return nil:
+    Rack::Utils.best_q_match("application/json", %w[text/html text/plain]).should.equal nil
   end
 
   should "escape html entities [&><'\"/]" do
@@ -378,6 +411,25 @@ describe Rack::Utils do
   should "return rfc2109 format from rfc2109 helper" do
     Rack::Utils.rfc2109(Time.at(0).gmtime).should == "Thu, 01-Jan-1970 00:00:00 GMT"
   end
+
+  should "clean directory traversal" do
+    Rack::Utils.clean_path_info("/cgi/../cgi/test").should.equal "/cgi/test"
+    Rack::Utils.clean_path_info(".").should.empty
+    Rack::Utils.clean_path_info("test/..").should.empty
+  end
+
+  should "clean unsafe directory traversal to safe path" do
+    Rack::Utils.clean_path_info("/../README.rdoc").should.equal "/README.rdoc"
+    Rack::Utils.clean_path_info("../test/spec_utils.rb").should.equal "test/spec_utils.rb"
+  end
+
+  should "not clean directory traversal with encoded periods" do
+    Rack::Utils.clean_path_info("/%2E%2E/README").should.equal "/%2E%2E/README"
+  end
+
+  should "clean slash only paths" do
+    Rack::Utils.clean_path_info("/").should.equal "/"
+  end
 end
 
 describe Rack::Utils, "byte_range" do
diff --git a/test/spec_webrick.rb b/test/spec_webrick.rb
index b29a82d5..497bfe20 100644
--- a/test/spec_webrick.rb
+++ b/test/spec_webrick.rb
@@ -162,5 +162,23 @@ describe Rack::Handler::WEBrick do
     }
   end
 
+  should "produce correct HTTP semantics with and without app chunking" do
+    @server.mount "/chunked", Rack::Handler::WEBrick,
+    Rack::Lint.new(lambda{ |req|
+      [
+        200,
+        {"Transfer-Encoding" => "chunked"},
+        ["7\r\nchunked\r\n0\r\n\r\n"]
+      ]
+    })
+
+    Net::HTTP.start(@host, @port){ |http|
+      res = http.get("/chunked")
+      res["Transfer-Encoding"].should.equal "chunked"
+      res["Content-Length"].should.equal nil
+      res.body.should.equal "chunked"
+    }
+  end
+
   @server.shutdown
 end