summary refs log tree commit
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--KNOWN-ISSUES14
-rw-r--r--README.rdoc11
-rw-r--r--SPEC4
-rw-r--r--lib/rack/auth/digest/md5.rb2
-rw-r--r--lib/rack/backports/uri/common_18.rb2
-rw-r--r--lib/rack/commonlogger.rb4
-rw-r--r--lib/rack/deflater.rb24
-rw-r--r--lib/rack/directory.rb10
-rw-r--r--lib/rack/handler/fastcgi.rb5
-rw-r--r--lib/rack/handler/mongrel.rb10
-rw-r--r--lib/rack/handler/scgi.rb5
-rw-r--r--lib/rack/handler/thin.rb10
-rw-r--r--lib/rack/handler/webrick.rb34
-rw-r--r--lib/rack/lint.rb4
-rw-r--r--lib/rack/methodoverride.rb2
-rw-r--r--lib/rack/mime.rb16
-rw-r--r--lib/rack/multipart/parser.rb4
-rw-r--r--lib/rack/request.rb10
-rw-r--r--lib/rack/response.rb1
-rw-r--r--lib/rack/sendfile.rb2
-rw-r--r--lib/rack/server.rb11
-rw-r--r--lib/rack/session/abstract/id.rb10
-rw-r--r--lib/rack/session/cookie.rb20
-rw-r--r--lib/rack/showstatus.rb2
-rw-r--r--lib/rack/static.rb57
-rw-r--r--lib/rack/utils.rb22
-rw-r--r--rack.gemspec6
-rw-r--r--test/multipart/invalid_character6
-rw-r--r--test/spec_builder.rb6
-rw-r--r--test/spec_commonlogger.rb26
-rw-r--r--test/spec_multipart.rb14
-rw-r--r--test/spec_request.rb19
-rw-r--r--test/spec_response.rb17
-rw-r--r--test/spec_session_cookie.rb26
-rw-r--r--test/spec_session_memcache.rb2
-rw-r--r--test/spec_showstatus.rb19
-rw-r--r--test/spec_webrick.rb23
38 files changed, 342 insertions, 121 deletions
diff --git a/.travis.yml b/.travis.yml
index 5336f3d2..68904373 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,6 +18,3 @@ branches:
 notifications:
   email: false
   irc: "irc.freenode.org#rack"
-matrix:
-  allow_failures:
-    - rvm: 2.0.0
diff --git a/KNOWN-ISSUES b/KNOWN-ISSUES
index e0373b2f..ceb2e61f 100644
--- a/KNOWN-ISSUES
+++ b/KNOWN-ISSUES
@@ -28,3 +28,17 @@
 
   Since lighttpd 1.4.23, you also can use the "fix-root-scriptname" flag
   in fastcgi.server.
+
+= Known conflicts regarding parameter parsing
+
+ * Many users have differing opinions about parameter parsing. The current
+   parameter parsers in Rack are based on a combination of the HTTP and CGI
+   specs, and are intended to round-trip encoding and decoding. There are some
+   choices that may be viewed as deficiencies, specifically:
+    - Rack does not create implicit arrays for multiple instances of a parameter
+    - Rack returns nil when a value is not given
+    - Rack does not support multi-type keys in parameters
+   These issues or choices, will not be fixed before 2.0, if at all. They are
+   very major breaking changes. Users are free to write alternative parameter
+   parsers, and their own Request and Response wrappers. Moreover, users are
+   encouraged to do so.
diff --git a/README.rdoc b/README.rdoc
index 02c1193f..7a3c8d58 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -555,12 +555,17 @@ The Rack Core Team, consisting of
 * Christian Neukirchen (chneukirchen)
 * James Tucker (raggi)
 * Josh Peek (josh)
+* José Valim (josevalim)
 * Michael Fellinger (manveru)
-* Ryan Tomayko (rtomayko)
-* Scytrin dai Kinthra (scytrin)
 * Aaron Patterson (tenderlove)
+* Santiago Pastorino (spastorino)
 * Konstantin Haase (rkh)
 
+and the Rack Alumnis
+
+* Ryan Tomayko (rtomayko)
+* Scytrin dai Kinthra (scytrin)
+
 would like to thank:
 
 * Adrian Madrid, for the LiteSpeed handler.
@@ -614,7 +619,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 == Links
 
-Rack:: <http://rack.github.com/>
+Rack:: <http://rack.github.io/>
 Official Rack repositories:: <http://github.com/rack>
 Rack Bug Tracking:: <http://github.com/rack/rack/issues>
 rack-devel mailing list:: <http://groups.google.com/group/rack-devel>
diff --git a/SPEC b/SPEC
index 9b28627c..3a6481d2 100644
--- a/SPEC
+++ b/SPEC
@@ -207,7 +207,7 @@ but only contain keys that consist of
 letters, digits, <tt>_</tt> or <tt>-</tt> and start with a letter.
 The values of the header must be Strings,
 consisting of lines (for multiple header values, e.g. multiple
-<tt>Set-Cookie</tt> values) seperated by "\n".
+<tt>Set-Cookie</tt> values) separated by "\n".
 The lines must not contain characters below 037.
 === The Content-Type
 There must not be a <tt>Content-Type</tt>, when the +Status+ is 1xx,
@@ -222,7 +222,7 @@ The Body itself should not be an instance of String, as this will
 break in Ruby 1.9.
 If the Body responds to +close+, it will be called after iteration. If
 the body is replaced by a middleware after action, the original body
-must be closed first, if it repsonds to close.
+must be closed first, if it responds to close.
 If the Body responds to +to_path+, it must return a String
 identifying the location of a file whose contents are identical
 to that produced by calling +each+; this may be used by the
diff --git a/lib/rack/auth/digest/md5.rb b/lib/rack/auth/digest/md5.rb
index b5871d77..ddee35de 100644
--- a/lib/rack/auth/digest/md5.rb
+++ b/lib/rack/auth/digest/md5.rb
@@ -96,7 +96,7 @@ module Rack
 
         def valid_digest?(auth)
           pw = @authenticator.call(auth.username)
-          pw && digest(auth, pw) == auth.response
+          pw && Rack::Utils.secure_compare(digest(auth, pw), auth.response)
         end
 
         def md5(data)
diff --git a/lib/rack/backports/uri/common_18.rb b/lib/rack/backports/uri/common_18.rb
index 0ea1998d..ca3a6360 100644
--- a/lib/rack/backports/uri/common_18.rb
+++ b/lib/rack/backports/uri/common_18.rb
@@ -46,7 +46,7 @@ module URI
 
   # Decode given +str+ of URL-encoded form data.
   #
-  # This decods + to SP.
+  # This decodes + to SP.
   #
   # See URI.encode_www_form_component, URI.decode_www_form
   def self.decode_www_form_component(str, enc=nil)
diff --git a/lib/rack/commonlogger.rb b/lib/rack/commonlogger.rb
index 7028615f..b3968ac7 100644
--- a/lib/rack/commonlogger.rb
+++ b/lib/rack/commonlogger.rb
@@ -18,7 +18,7 @@ module Rack
   class CommonLogger
     # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
     #
-    #   lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
+    #   lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
     #
     #   %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
     FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
@@ -46,7 +46,7 @@ module Rack
       logger.write FORMAT % [
         env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
         env["REMOTE_USER"] || "-",
-        now.strftime("%d/%b/%Y %H:%M:%S"),
+        now.strftime("%d/%b/%Y %H:%M:%S %z"),
         env["REQUEST_METHOD"],
         env["PATH_INFO"],
         env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
diff --git a/lib/rack/deflater.rb b/lib/rack/deflater.rb
index 2f219f0e..fe2ac3db 100644
--- a/lib/rack/deflater.rb
+++ b/lib/rack/deflater.rb
@@ -68,6 +68,7 @@ module Rack
       def initialize(body, mtime)
         @body = body
         @mtime = mtime
+        @closed = false
       end
 
       def each(&block)
@@ -79,7 +80,7 @@ module Rack
           gzip.flush
         }
       ensure
-        @body.close if @body.respond_to?(:close)
+        close
         gzip.close
         @writer = nil
       end
@@ -87,6 +88,12 @@ module Rack
       def write(data)
         @writer.call(data)
       end
+
+      def close
+        return if @closed
+        @closed = true
+        @body.close if @body.respond_to?(:close)
+      end
     end
 
     class DeflateStream
@@ -100,16 +107,23 @@ module Rack
 
       def initialize(body)
         @body = body
+        @closed = false
       end
 
       def each
-        deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
-        @body.each { |part| yield deflater.deflate(part, Zlib::SYNC_FLUSH) }
-        yield deflater.finish
+        deflator = ::Zlib::Deflate.new(*DEFLATE_ARGS)
+        @body.each { |part| yield deflator.deflate(part, Zlib::SYNC_FLUSH) }
+        yield deflator.finish
         nil
       ensure
+        close
+        deflator.close
+      end
+
+      def close
+        return if @closed
+        @closed = true
         @body.close if @body.respond_to?(:close)
-        deflater.close
       end
     end
   end
diff --git a/lib/rack/directory.rb b/lib/rack/directory.rb
index e90ee082..602f2d44 100644
--- a/lib/rack/directory.rb
+++ b/lib/rack/directory.rb
@@ -135,8 +135,8 @@ table { width:100%%; }
     end
 
     def each
-      show_path = @path.sub(/^#{@root}/,'')
-      files = @files.map{|f| DIR_FILE % f }*"\n"
+      show_path = Rack::Utils.escape_html(@path.sub(/^#{@root}/,''))
+      files = @files.map{|f| DIR_FILE % DIR_FILE_escape(*f) }*"\n"
       page  = DIR_PAGE % [ show_path, show_path , files ]
       page.each_line{|l| yield l }
     end
@@ -157,5 +157,11 @@ table { width:100%%; }
 
       int.to_s + 'B'
     end
+
+    private
+    # Assumes url is already escaped.
+    def DIR_FILE_escape url, *html
+      [url, *html.map { |e| Utils.escape_html(e) }]
+    end
   end
 end
diff --git a/lib/rack/handler/fastcgi.rb b/lib/rack/handler/fastcgi.rb
index 340e3613..b26fabc3 100644
--- a/lib/rack/handler/fastcgi.rb
+++ b/lib/rack/handler/fastcgi.rb
@@ -30,8 +30,11 @@ module Rack
       end
 
       def self.valid_options
+        environment  = ENV['RACK_ENV'] || 'development'
+        default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
         {
-          "Host=HOST" => "Hostname to listen on (default: localhost)",
+          "Host=HOST" => "Hostname to listen on (default: #{default_host})",
           "Port=PORT" => "Port to listen on (default: 8080)",
           "File=PATH" => "Creates a Domain socket at PATH instead of a TCP socket. Ignores Host and Port if set.",
         }
diff --git a/lib/rack/handler/mongrel.rb b/lib/rack/handler/mongrel.rb
index 1a702fd2..20be86b1 100644
--- a/lib/rack/handler/mongrel.rb
+++ b/lib/rack/handler/mongrel.rb
@@ -7,8 +7,11 @@ module Rack
   module Handler
     class Mongrel < ::Mongrel::HttpHandler
       def self.run(app, options={})
+        environment  = ENV['RACK_ENV'] || 'development'
+        default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
         server = ::Mongrel::HttpServer.new(
-          options[:Host]           || '0.0.0.0',
+          options[:Host]           || default_host,
           options[:Port]           || 8080,
           options[:num_processors] || 950,
           options[:throttle]       || 0,
@@ -39,8 +42,11 @@ module Rack
       end
 
       def self.valid_options
+        environment  = ENV['RACK_ENV'] || 'development'
+        default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
         {
-          "Host=HOST" => "Hostname to listen on (default: localhost)",
+          "Host=HOST" => "Hostname to listen on (default: #{default_host})",
           "Port=PORT" => "Port to listen on (default: 8080)",
           "Processors=N" => "Number of concurrent processors to accept (default: 950)",
           "Timeout=N" => "Time before a request is dropped for inactivity (default: 60)",
diff --git a/lib/rack/handler/scgi.rb b/lib/rack/handler/scgi.rb
index a4fe6cea..40e86fb9 100644
--- a/lib/rack/handler/scgi.rb
+++ b/lib/rack/handler/scgi.rb
@@ -17,8 +17,11 @@ module Rack
       end
 
       def self.valid_options
+        environment  = ENV['RACK_ENV'] || 'development'
+        default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
         {
-          "Host=HOST" => "Hostname to listen on (default: localhost)",
+          "Host=HOST" => "Hostname to listen on (default: #{default_host})",
           "Port=PORT" => "Port to listen on (default: 8080)",
         }
       end
diff --git a/lib/rack/handler/thin.rb b/lib/rack/handler/thin.rb
index dc269725..704db06c 100644
--- a/lib/rack/handler/thin.rb
+++ b/lib/rack/handler/thin.rb
@@ -6,7 +6,10 @@ module Rack
   module Handler
     class Thin
       def self.run(app, options={})
-        host = options.delete(:Host) || '0.0.0.0'
+        environment  = ENV['RACK_ENV'] || 'development'
+        default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
+        host = options.delete(:Host) || default_host
         port = options.delete(:Port) || 8080
         args = [host, port, app, options]
         # Thin versions below 0.8.0 do not support additional options
@@ -17,8 +20,11 @@ module Rack
       end
 
       def self.valid_options
+        environment  = ENV['RACK_ENV'] || 'development'
+        default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
         {
-          "Host=HOST" => "Hostname to listen on (default: localhost)",
+          "Host=HOST" => "Hostname to listen on (default: #{default_host})",
           "Port=PORT" => "Port to listen on (default: 8080)",
         }
       end
diff --git a/lib/rack/handler/webrick.rb b/lib/rack/handler/webrick.rb
index 487a0ea1..f76679b4 100644
--- a/lib/rack/handler/webrick.rb
+++ b/lib/rack/handler/webrick.rb
@@ -6,8 +6,12 @@ module Rack
   module Handler
     class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
       def self.run(app, options={})
-        options[:BindAddress] = options.delete(:Host) if options[:Host]
+        environment  = ENV['RACK_ENV'] || 'development'
+        default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
+        options[:BindAddress] = options.delete(:Host) || default_host
         options[:Port] ||= 8080
+        options[:OutputBufferSize] = 5
         @server = ::WEBrick::HTTPServer.new(options)
         @server.mount "/", Rack::Handler::WEBrick, app
         yield @server  if block_given?
@@ -15,8 +19,11 @@ module Rack
       end
 
       def self.valid_options
+        environment  = ENV['RACK_ENV'] || 'development'
+        default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
         {
-          "Host=HOST" => "Hostname to listen on (default: localhost)",
+          "Host=HOST" => "Hostname to listen on (default: #{default_host})",
           "Port=PORT" => "Port to listen on (default: 8080)",
         }
       end
@@ -46,7 +53,11 @@ module Rack
                      "rack.multiprocess" => false,
                      "rack.run_once" => false,
 
-                     "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
+                     "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http",
+
+                     "rack.hijack?" => true,
+                     "rack.hijack" => lambda { raise NotImplementedError, "only partial hijack is supported."},
+                     "rack.hijack_io" => nil,
                    })
 
         env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
@@ -61,6 +72,8 @@ module Rack
         begin
           res.status = status.to_i
           headers.each { |k, vs|
+            next if k.downcase == "rack.hijack"
+
             if k.downcase == "set-cookie"
               res.cookies.concat vs.split("\n")
             else
@@ -69,9 +82,18 @@ module Rack
               res[k] = vs.split("\n").join(", ")
             end
           }
-          body.each { |part|
-            res.body << part
-          }
+
+          io_lambda = headers["rack.hijack"]
+          if io_lambda
+            rd, wr = IO.pipe
+            res.body = rd
+            res.chunked = true
+            io_lambda.call wr
+          else
+            body.each { |part|
+              res.body << part
+            }
+          end
         ensure
           body.close  if body.respond_to? :close
         end
diff --git a/lib/rack/lint.rb b/lib/rack/lint.rb
index fd21f775..0c6e1a35 100644
--- a/lib/rack/lint.rb
+++ b/lib/rack/lint.rb
@@ -584,7 +584,7 @@ module Rack
         assert("a header value must be a String, but the value of " +
           "'#{key}' is a #{value.class}") { value.kind_of? String }
         ## consisting of lines (for multiple header values, e.g. multiple
-        ## <tt>Set-Cookie</tt> values) seperated by "\n".
+        ## <tt>Set-Cookie</tt> values) separated by "\n".
         value.split("\n").each { |item|
           ## The lines must not contain characters below 037.
           assert("invalid header value #{key}: #{item.inspect}") {
@@ -660,7 +660,7 @@ module Rack
       ##
       ## If the Body responds to +close+, it will be called after iteration. If
       ## the body is replaced by a middleware after action, the original body
-      ## must be closed first, if it repsonds to close.
+      ## must be closed first, if it responds to close.
       # XXX howto: assert("Body has not been closed") { @closed }
 
 
diff --git a/lib/rack/methodoverride.rb b/lib/rack/methodoverride.rb
index 1bdaca84..449961ce 100644
--- a/lib/rack/methodoverride.rb
+++ b/lib/rack/methodoverride.rb
@@ -1,6 +1,6 @@
 module Rack
   class MethodOverride
-    HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS PATCH)
+    HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK)
 
     METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
     HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
diff --git a/lib/rack/mime.rb b/lib/rack/mime.rb
index 5d050221..2879c05f 100644
--- a/lib/rack/mime.rb
+++ b/lib/rack/mime.rb
@@ -29,21 +29,7 @@ module Rack
       v1, v2 = value.split('/', 2)
       m1, m2 = matcher.split('/', 2)
 
-      if m1 == '*'
-        if m2.nil? || m2 == '*'
-          return true
-        elsif m2 == v2
-          return true
-        else
-          return false
-        end
-      end
-
-      return false if v1 != m1
-
-      return true if m2.nil? || m2 == '*'
-
-      m2 == v2
+      (m1 == '*' || v1 == m1) && (m2.nil? || m2 == '*' || m2 == v2)
     end
     module_function :match?
 
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb
index 1315c7b3..a8019f98 100644
--- a/lib/rack/multipart/parser.rb
+++ b/lib/rack/multipart/parser.rb
@@ -87,7 +87,6 @@ module Rack
         head = nil
         body = ''
         filename = content_type = name = nil
-        content = nil
 
         until head && @buf =~ rx
           if !head && i = @buf.index(EOL+EOL)
@@ -137,6 +136,9 @@ module Rack
         if filename && filename.scan(/%.?.?/).all? { |s| s =~ /%[0-9a-fA-F]{2}/ }
           filename = Utils.unescape(filename)
         end
+        if filename.respond_to?(:valid_encoding?) && !filename.valid_encoding?
+          filename = filename.chars.select { |char| char.valid_encoding? }.join
+        end
         if filename && filename !~ /\\[^\\"]/
           filename = filename.gsub(/\\(.)/, '\1')
         end
diff --git a/lib/rack/request.rb b/lib/rack/request.rb
index e8734d76..f3a048ab 100644
--- a/lib/rack/request.rb
+++ b/lib/rack/request.rb
@@ -100,6 +100,8 @@ module Rack
         port.to_i
       elsif @env.has_key?("HTTP_X_FORWARDED_HOST")
         DEFAULT_PORTS[scheme]
+      elsif @env.has_key?("HTTP_X_FORWARDED_PROTO")
+        DEFAULT_PORTS[@env['HTTP_X_FORWARDED_PROTO']]
       else
         @env["SERVER_PORT"].to_i
       end
@@ -126,6 +128,9 @@ module Rack
     # Checks the HTTP request method (or verb) to see if it was of type OPTIONS
     def options?; request_method == "OPTIONS" end
 
+    # Checks the HTTP request method (or verb) to see if it was of type LINK
+    def link?;    request_method == "LINK"    end
+
     # Checks the HTTP request method (or verb) to see if it was of type PATCH
     def patch?;   request_method == "PATCH"   end
 
@@ -137,6 +142,9 @@ 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
 
 
     # The set of form-data media-types. Requests that do not indicate
@@ -199,7 +207,6 @@ module Rack
       elsif @env["rack.request.form_input"].eql? @env["rack.input"]
         @env["rack.request.form_hash"]
       elsif form_data? || parseable_data?
-        @env["rack.request.form_input"] = @env["rack.input"]
         unless @env["rack.request.form_hash"] = parse_multipart(env)
           form_vars = @env["rack.input"].read
 
@@ -212,6 +219,7 @@ module Rack
 
           @env["rack.input"].rewind
         end
+        @env["rack.request.form_input"] = @env["rack.input"]
         @env["rack.request.form_hash"]
       else
         {}
diff --git a/lib/rack/response.rb b/lib/rack/response.rb
index 2beba7a8..2076aff0 100644
--- a/lib/rack/response.rb
+++ b/lib/rack/response.rb
@@ -122,6 +122,7 @@ module Rack
 
       def ok?;                 status == 200;                        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
diff --git a/lib/rack/sendfile.rb b/lib/rack/sendfile.rb
index bc04ca2f..c247a3bc 100644
--- a/lib/rack/sendfile.rb
+++ b/lib/rack/sendfile.rb
@@ -22,7 +22,7 @@ module Rack
   #
   # Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile
   # but requires parts of the filesystem to be mapped into a private URL
-  # hierarachy.
+  # hierarchy.
   #
   # The following example shows the Nginx configuration required to create
   # a private "/files/" area, enable X-Accel-Redirect, and pass the special
diff --git a/lib/rack/server.rb b/lib/rack/server.rb
index dfaed3fc..be7014c6 100644
--- a/lib/rack/server.rb
+++ b/lib/rack/server.rb
@@ -66,7 +66,7 @@ module Rack
             options[:daemonize] = d ? true : false
           }
 
-          opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f|
+          opts.on("-P", "--pid FILE", "file to store PID") { |f|
             options[:pid] = ::File.expand_path(f)
           }
 
@@ -185,11 +185,14 @@ module Rack
     end
 
     def default_options
+      environment  = ENV['RACK_ENV'] || 'development'
+      default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
+
       {
-        :environment => ENV['RACK_ENV'] || "development",
+        :environment => environment,
         :pid         => nil,
         :Port        => 9292,
-        :Host        => "0.0.0.0",
+        :Host        => default_host,
         :AccessLog   => [],
         :config      => "config.ru"
       }
@@ -350,6 +353,8 @@ module Rack
         return :exited unless ::File.exist?(options[:pid])
 
         pid = ::File.read(options[:pid]).to_i
+        return :dead if pid == 0
+
         Process.kill(0, pid)
         :running
       rescue Errno::ESRCH
diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb
index 0e0ad351..e9edeb7f 100644
--- a/lib/rack/session/abstract/id.rb
+++ b/lib/rack/session/abstract/id.rb
@@ -289,7 +289,7 @@ module Rack
           value && !value.empty?
         end
 
-        # Session should be commited if it was loaded, any of specific options like :renew, :drop
+        # Session should be committed if it was loaded, any of specific options like :renew, :drop
         # or :expire_after was given and the security permissions match. Skips if skip is given.
 
         def commit_session?(env, session, options)
@@ -342,7 +342,7 @@ module Rack
           if not data = set_session(env, session_id, session_data, options)
             env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
           elsif options[:defer] and not options[:renew]
-            env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
+            env["rack.errors"].puts("Deferring cookie for #{session_id}") if $VERBOSE
           else
             cookie = Hash.new
             cookie[:value] = data
@@ -369,7 +369,7 @@ module Rack
           SessionHash
         end
 
-        # All thread safety and session retrival proceedures should occur here.
+        # All thread safety and session retrieval procedures should occur here.
         # Should return [session_id, session].
         # If nil is provided as the session id, generation of a new valid id
         # should occur within.
@@ -378,7 +378,7 @@ module Rack
           raise '#get_session not implemented.'
         end
 
-        # All thread safety and session storage proceedures should occur here.
+        # All thread safety and session storage procedures should occur here.
         # Must return the session id if the session was saved successfully, or
         # false if the session could not be saved.
 
@@ -386,7 +386,7 @@ module Rack
           raise '#set_session not implemented.'
         end
 
-        # All thread safety and session destroy proceedures should occur here.
+        # All thread safety and session destroy procedures should occur here.
         # Should return a new session id or nil if options[:drop]
 
         def destroy_session(env, sid, options)
diff --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb
index 5aa80cb6..22e33d1f 100644
--- a/lib/rack/session/cookie.rb
+++ b/lib/rack/session/cookie.rb
@@ -1,4 +1,5 @@
 require 'openssl'
+require 'zlib'
 require 'rack/request'
 require 'rack/response'
 require 'rack/session/abstract/id'
@@ -78,6 +79,19 @@ module Rack
             ::Rack::Utils::OkJson.decode(super(str)) rescue nil
           end
         end
+
+        class ZipJSON < Base64
+          def encode(obj)
+            super(Zlib::Deflate.deflate(::Rack::Utils::OkJson.encode(obj)))
+          end
+
+          def decode(str)
+            return unless str
+            ::Rack::Utils::OkJson.decode(Zlib::Inflate.inflate(super(str)))
+          rescue
+            nil
+          end
+        end
       end
 
       # Use no encoding for session cookies
@@ -86,12 +100,6 @@ module Rack
         def decode(str); str; end
       end
 
-      # Reverse string encoding. (trollface)
-      class Reverse
-        def encode(str); str.reverse; end
-        def decode(str); str.reverse; end
-      end
-
       attr_reader :coder
 
       def initialize(app, options={})
diff --git a/lib/rack/showstatus.rb b/lib/rack/showstatus.rb
index 5a9506f2..6892a5b7 100644
--- a/lib/rack/showstatus.rb
+++ b/lib/rack/showstatus.rb
@@ -96,7 +96,7 @@ TEMPLATE = <<'HTML'
     </table>
   </div>
   <div id="info">
-    <p><%= detail %></p>
+    <p><%=h detail %></p>
   </div>
 
   <div id="explanation">
diff --git a/lib/rack/static.rb b/lib/rack/static.rb
index 46bc66da..41aec7f3 100644
--- a/lib/rack/static.rb
+++ b/lib/rack/static.rb
@@ -90,9 +90,8 @@ module Rack
       @header_rules = options[:header_rules] || []
       # Allow for legacy :cache_control option while prioritizing global header_rules setting
       @header_rules.insert(0, [:all, {'Cache-Control' => options[:cache_control]}]) if options[:cache_control]
-      @headers = {}
 
-      @file_server = Rack::File.new(root, @headers)
+      @file_server = Rack::File.new(root)
     end
 
     def overwrite_file_path(path)
@@ -112,42 +111,40 @@ module Rack
 
       if can_serve(path)
         env["PATH_INFO"] = (path =~ /\/$/ ? path + @index : @urls[path]) if overwrite_file_path(path)
-        @path = env["PATH_INFO"]
-        apply_header_rules
-        @file_server.call(env)
+        path = env["PATH_INFO"]
+        response = @file_server.call(env)
+
+        headers = response[1]
+        applicable_rules(path).each do |rule, new_headers|
+          new_headers.each { |field, content| headers[field] = content }
+        end
+
+        response
       else
         @app.call(env)
       end
     end
 
     # Convert HTTP header rules to HTTP headers
-    def apply_header_rules
-      @header_rules.each do |rule, headers|
-        apply_rule(rule, headers)
-      end
-    end
-
-    def apply_rule(rule, headers)
-      case rule
-      when :all    # All files
-        set_headers(headers)
-      when :fonts  # Fonts Shortcut
-        set_headers(headers) if @path.match(/\.(?:ttf|otf|eot|woff|svg)\z/)
-      when String  # Folder
-        path = ::Rack::Utils.unescape(@path)
-        set_headers(headers) if (path.start_with?(rule) || path.start_with?('/' + rule))
-      when Array   # Extension/Extensions
-        extensions = rule.join('|')
-        set_headers(headers) if @path.match(/\.(#{extensions})\z/)
-      when Regexp  # Flexible Regexp
-        set_headers(headers) if @path.match(rule)
-      else
+    def applicable_rules(path)
+      @header_rules.find_all do |rule, new_headers|
+        case rule
+        when :all
+          true
+        when :fonts
+          path =~ /\.(?:ttf|otf|eot|woff|svg)\z/
+        when String
+          path = ::Rack::Utils.unescape(path)
+          path.start_with?(rule) || path.start_with?('/' + rule)
+        when Array
+          path =~ /\.(#{rule.join('|')})\z/
+        when Regexp
+          path =~ rule
+        else
+          false
+        end
       end
     end
 
-    def set_headers(headers)
-      headers.each { |field, content| @headers[field] = content }
-    end
-
   end
 end
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 561e46e5..43bbef37 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -203,7 +203,7 @@ module Rack
     if //.respond_to?(:encoding)
       ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
     else
-      # On 1.8, there is a kcode = 'u' bug that allows for XSS otherwhise
+      # On 1.8, there is a kcode = 'u' bug that allows for XSS otherwise
       # TODO doesn't apply to jruby, so a better condition above might be preferable?
       ESCAPE_HTML_PATTERN = /#{Regexp.union(*ESCAPE_HTML.keys)}/n
     end
@@ -234,10 +234,8 @@ module Rack
         encoding_candidates.push("identity")
       end
 
-      expanded_accept_encoding.find_all { |m, q|
-        q == 0.0
-      }.each { |m, _|
-        encoding_candidates.delete(m)
+      expanded_accept_encoding.each { |m, q|
+        encoding_candidates.delete(m) if q == 0.0
       }
 
       return (encoding_candidates & available_encodings)[0]
@@ -276,7 +274,7 @@ module Rack
         expires = "; expires=" +
           rfc2822(value[:expires].clone.gmtime) if value[:expires]
         secure = "; secure"  if value[:secure]
-        httponly = "; HttpOnly" if value[:httponly]
+        httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
         value = value[:value]
       end
       value = [value] unless Array === value
@@ -350,7 +348,7 @@ module Rack
     # of '% %b %Y'.
     # It assumes that the time is in GMT to comply to the RFC 2109.
     #
-    # NOTE: I'm not sure the RFC says it requires GMT, but is ambigous enough
+    # NOTE: I'm not sure the RFC says it requires GMT, but is ambiguous enough
     # that I'm certain someone implemented only that option.
     # Do not use %a and %b from Time.strptime, it would use localized names for
     # weekday and month.
@@ -538,9 +536,9 @@ module Rack
 
     # Every standard HTTP code mapped to the appropriate message.
     # Generated with:
-    # irb -ropen-uri -rnokogiri
-    # > Nokogiri::XML(open("http://www.iana.org/assignments/http-status-codes/http-status-codes.xml")).css("record").each{|r|
-    #         puts "#{r.css('value').text} => '#{r.css('description').text}'"}
+    # ruby -ropen-uri -rnokogiri -e "Nokogiri::XML(open(
+    #   'http://www.iana.org/assignments/http-status-codes/http-status-codes.xml')).css('record').each{|r|
+    #   name = r.css('description').text; puts %Q[#{r.css('value').text} => '#{name}',] unless name == 'Unassigned' }"
     HTTP_STATUS_CODES = {
       100 => 'Continue',
       101 => 'Switching Protocols',
@@ -585,12 +583,9 @@ module Rack
       422 => 'Unprocessable Entity',
       423 => 'Locked',
       424 => 'Failed Dependency',
-      425 => 'Reserved for WebDAV advanced collections expired proposal',
       426 => 'Upgrade Required',
-      427 => 'Unassigned',
       428 => 'Precondition Required',
       429 => 'Too Many Requests',
-      430 => 'Unassigned',
       431 => 'Request Header Fields Too Large',
       500 => 'Internal Server Error',
       501 => 'Not Implemented',
@@ -601,7 +596,6 @@ module Rack
       506 => 'Variant Also Negotiates (Experimental)',
       507 => 'Insufficient Storage',
       508 => 'Loop Detected',
-      509 => 'Unassigned',
       510 => 'Not Extended',
       511 => 'Network Authentication Required'
     }
diff --git a/rack.gemspec b/rack.gemspec
index 388d6eda..6ab4dd57 100644
--- a/rack.gemspec
+++ b/rack.gemspec
@@ -1,6 +1,6 @@
 Gem::Specification.new do |s|
   s.name            = "rack"
-  s.version         = "1.5.2"
+  s.version         = "1.6.0.alpha" # Ahead of 1.5, modify before release
   s.platform        = Gem::Platform::RUBY
   s.summary         = "a modular Ruby webserver interface"
   s.license         = "MIT"
@@ -12,7 +12,7 @@ the simplest way possible, it unifies and distills the API for web
 servers, web frameworks, and software in between (the so-called
 middleware) into a single method call.
 
-Also see http://rack.github.com/.
+Also see http://rack.github.io/.
 EOF
 
   s.files           = Dir['{bin/*,contrib/*,example/*,lib/**/*,test/**/*}'] +
@@ -25,7 +25,7 @@ EOF
 
   s.author          = 'Christian Neukirchen'
   s.email           = 'chneukirchen@gmail.com'
-  s.homepage        = 'http://rack.github.com/'
+  s.homepage        = 'http://rack.github.io/'
   s.rubyforge_project = 'rack'
 
   s.add_development_dependency 'bacon'
diff --git a/test/multipart/invalid_character b/test/multipart/invalid_character
new file mode 100644
index 00000000..82467181
--- /dev/null
+++ b/test/multipart/invalid_character
@@ -0,0 +1,6 @@
+--AaB03x
+Content-Disposition: form-data; name="files"; filename="invalidÃ.txt"
+Content-Type: text/plain
+
+contents
+--AaB03x--
diff --git a/test/spec_builder.rb b/test/spec_builder.rb
index a2fd5685..0774f597 100644
--- a/test/spec_builder.rb
+++ b/test/spec_builder.rb
@@ -180,8 +180,7 @@ describe Rack::Builder do
     end
 
     it "removes __END__ before evaluating app" do
-      app, options = Rack::Builder.parse_file config_file('end.ru')
-      options = nil # ignored, prevents warning
+      app, _ = Rack::Builder.parse_file config_file('end.ru')
       Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
     end
 
@@ -199,8 +198,7 @@ describe Rack::Builder do
     end
 
     it "sets __LINE__ correctly" do
-      app, options = Rack::Builder.parse_file config_file('line.ru')
-      options = nil # ignored, prevents warning
+      app, _ = Rack::Builder.parse_file config_file('line.ru')
       Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1'
     end
   end
diff --git a/test/spec_commonlogger.rb b/test/spec_commonlogger.rb
index d88e19c3..9c96638c 100644
--- a/test/spec_commonlogger.rb
+++ b/test/spec_commonlogger.rb
@@ -47,6 +47,32 @@ describe Rack::CommonLogger do
     res.errors.should =~ /"GET \/ " 200 - /
   end
 
+  def with_mock_time(t = 0)
+    mc = class <<Time; self; end
+    mc.send :alias_method, :old_now, :now
+    mc.send :define_method, :now do
+      at(t)
+    end
+    yield
+  ensure
+    mc.send :alias_method, :now, :old_now
+  end
+
+  should "log in common log format" do
+    log = StringIO.new
+    with_mock_time do
+      Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
+    end
+
+    md = /- - - \[([^\]]+)\] "(\w+) \/ " (\d{3}) \d+ ([\d\.]+)/.match(log.string)
+    md.should.not == nil
+    time, method, status, duration = *md.captures
+    time.should == Time.at(0).strftime("%d/%b/%Y %H:%M:%S %z")
+    method.should == "GET"
+    status.should == "200"
+    (0..1).should.include?(duration.to_f)
+  end
+
   def length
     123
   end
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index bd0b07b6..fc180c24 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -166,6 +166,20 @@ describe Rack::Multipart do
     params["files"][:tempfile].read.should.equal "contents"
   end
 
+  should "parse multipart upload with filename with invalid characters" do
+    env = Rack::MockRequest.env_for("/", multipart_fixture(:invalid_character))
+    params = Rack::Multipart.parse_multipart(env)
+    params["files"][:type].should.equal "text/plain"
+    params["files"][:filename].should.match(/invalid/)
+    head = "Content-Disposition: form-data; " +
+      "name=\"files\"; filename=\"invalid\xC3.txt\"\r\n" +
+      "Content-Type: text/plain\r\n"
+    head = head.force_encoding("ASCII-8BIT") if head.respond_to?(:force_encoding)
+    params["files"][:head].should.equal head
+    params["files"][:name].should.equal "files"
+    params["files"][:tempfile].read.should.equal "contents"
+  end
+
   should "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_request.rb b/test/spec_request.rb
index 9649c5d2..039aae6b 100644
--- a/test/spec_request.rb
+++ b/test/spec_request.rb
@@ -93,6 +93,11 @@ describe Rack::Request do
     req = Rack::Request.new \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
     req.port.should.equal 80
+
+    req = Rack::Request.new \
+      Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https", "SERVER_PORT" => "80")
+
+    req.port.should.equal 443
   end
 
   should "figure out the correct host with port" do
@@ -774,6 +779,20 @@ EOF
     lambda { req.POST }.should.raise(EOFError)
   end
 
+  should "consistently raise EOFError on bad multipart form data" do
+    input = <<EOF
+--AaB03x\r
+content-disposition: form-data; name="huge"; filename="huge"\r
+EOF
+    req = Rack::Request.new Rack::MockRequest.env_for("/",
+                      "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
+                      "CONTENT_LENGTH" => input.size,
+                      :input => input)
+
+    lambda { req.POST }.should.raise(EOFError)
+    lambda { req.POST }.should.raise(EOFError)
+  end
+
   should "correctly parse the part name from Content-Id header" do
     input = <<EOF
 --AaB03x\r
diff --git a/test/spec_response.rb b/test/spec_response.rb
index 7ba1e0e1..12b8b7b3 100644
--- a/test/spec_response.rb
+++ b/test/spec_response.rb
@@ -85,6 +85,18 @@ describe Rack::Response do
     response["Set-Cookie"].should.equal "foo=bar; HttpOnly"
   end
 
+  it "can set http only cookies with :http_only" do
+    response = Rack::Response.new
+    response.set_cookie "foo", {:value => "bar", :http_only => true}
+    response["Set-Cookie"].should.equal "foo=bar; HttpOnly"
+  end
+
+  it "can set prefers :httponly for http only cookie setting when :httponly and :http_only provided" do
+    response = Rack::Response.new
+    response.set_cookie "foo", {:value => "bar", :httponly => false, :http_only => true}
+    response["Set-Cookie"].should.equal "foo=bar"
+  end
+
   it "can delete cookies" do
     response = Rack::Response.new
     response.set_cookie "foo", "bar"
@@ -216,6 +228,11 @@ describe Rack::Response do
     res.should.be.client_error
     res.should.be.bad_request
 
+    res.status = 401
+    res.should.not.be.successful
+    res.should.be.client_error
+    res.should.be.unauthorized
+
     res.status = 404
     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 8256f762..f5d69b16 100644
--- a/test/spec_session_cookie.rb
+++ b/test/spec_session_cookie.rb
@@ -119,13 +119,35 @@ describe Rack::Session::Cookie do
         coder.decode('lulz').should.equal nil
       end
     end
+
+    describe 'ZipJSON' do
+      it 'jsons, deflates, and base64 encodes' do
+        coder = Rack::Session::Cookie::Base64::ZipJSON.new
+        obj   = %w[fuuuuu]
+        json = Rack::Utils::OkJson.encode(obj)
+        coder.encode(obj).should.equal [Zlib::Deflate.deflate(json)].pack('m')
+      end
+
+      it 'base64 decodes, inflates, and decodes json' do
+        coder = Rack::Session::Cookie::Base64::ZipJSON.new
+        obj   = %w[fuuuuu]
+        json  = Rack::Utils::OkJson.encode(obj)
+        b64   = [Zlib::Deflate.deflate(json)].pack('m')
+        coder.decode(b64).should.equal obj
+      end
+
+      it 'rescues failures on decode' do
+        coder = Rack::Session::Cookie::Base64::ZipJSON.new
+        coder.decode('lulz').should.equal nil
+      end
+    end
   end
 
   it "warns if no secret is given" do
-    cookie = Rack::Session::Cookie.new(incrementor)
+    Rack::Session::Cookie.new(incrementor)
     @warnings.first.should =~ /no secret/i
     @warnings.clear
-    cookie = Rack::Session::Cookie.new(incrementor, :secret => 'abc')
+    Rack::Session::Cookie.new(incrementor, :secret => 'abc')
     @warnings.should.be.empty?
   end
 
diff --git a/test/spec_session_memcache.rb b/test/spec_session_memcache.rb
index 6e5325b5..2b759806 100644
--- a/test/spec_session_memcache.rb
+++ b/test/spec_session_memcache.rb
@@ -274,7 +274,7 @@ begin
       session['counter'].should.equal 2 # meeeh
 
       tnum = rand(7).to_i+5
-      r = Array.new(tnum) do |i|
+      r = Array.new(tnum) do
         app = Rack::Utils::Context.new pool, time_delta
         req = Rack::MockRequest.new app
         Thread.new(req) do |run|
diff --git a/test/spec_showstatus.rb b/test/spec_showstatus.rb
index 6f8e6fe1..5d97e8e5 100644
--- a/test/spec_showstatus.rb
+++ b/test/spec_showstatus.rb
@@ -1,6 +1,7 @@
 require 'rack/showstatus'
 require 'rack/lint'
 require 'rack/mock'
+require 'rack/utils'
 
 describe Rack::ShowStatus do
   def show_status(app)
@@ -40,6 +41,24 @@ describe Rack::ShowStatus do
     res.should =~ /too meta/
   end
 
+  should "escape error" do
+    detail = "<script>alert('hi \"')</script>"
+    req = Rack::MockRequest.new(
+      show_status(
+        lambda{|env|
+          env["rack.showstatus.detail"] = detail
+          [500, {"Content-Type" => "text/plain", "Content-Length" => "0"}, []]
+    }))
+
+    res = req.get("/", :lint => true)
+    res.should.be.not.empty
+
+    res["Content-Type"].should.equal("text/html")
+    res.should =~ /500/
+    res.should.not.include detail
+    res.body.should.include Rack::Utils.escape_html(detail)
+  end
+
   should "not replace existing messages" do
     req = Rack::MockRequest.new(
       show_status(
diff --git a/test/spec_webrick.rb b/test/spec_webrick.rb
index 5d647724..b29a82d5 100644
--- a/test/spec_webrick.rb
+++ b/test/spec_webrick.rb
@@ -139,5 +139,28 @@ describe Rack::Handler::WEBrick do
     }
   end
 
+  should "support Rack partial hijack" do
+    io_lambda = lambda{ |io|
+      5.times do
+        io.write "David\r\n"
+      end
+      io.close
+    }
+
+    @server.mount "/partial", Rack::Handler::WEBrick,
+    Rack::Lint.new(lambda{ |req|
+      [
+        200,
+        {"rack.hijack" => io_lambda},
+        [""]
+      ]
+    })
+
+    Net::HTTP.start(@host, @port){ |http|
+      res = http.get("/partial")
+      res.body.should.equal "David\r\nDavid\r\nDavid\r\nDavid\r\nDavid\r\n"
+    }
+  end
+
   @server.shutdown
 end