summary refs log tree commit
diff options
context:
space:
mode:
authorschneems <richard.schneeman@gmail.com>2014-10-01 18:09:37 -0500
committerschneems <richard.schneeman@gmail.com>2014-10-02 07:42:20 -0500
commitdc53a8c26dc55d21240233b3d83d36efdef6e924 (patch)
tree991037ede0df148b5aef7137f321591401f98d53
parenta71be3c914c10d1089238f4e21b029b885be4029 (diff)
downloadrack-dc53a8c26dc55d21240233b3d83d36efdef6e924.tar.gz
Less allocated objects on each request
How many? Using `memory_profiler` and a Rails app (codetriage.com), master uses:

```
rack/lib x 7318
```

After this patch, the app uses:

```
rack/lib x 4598
```

Or `(7318 - 4598) / 7318.0 * 100 # => 37.16` % fewer objects __PER REQUEST__.

To do this, I extracted really commonly used strings into top level Rack constants. It makes for a bit of a big diff, but I believe the changes are worth it. 

Running benchmark/ips against the same app, I'm seeing a performance host of `2~4%` across the entire app response. This doesn't just make Rack faster, it will make your app faster.

While we could certainly go overboard and pre-define ALL strings as constants, that would be pretty gnarly to work with. This patch goes after the largest of the low hanging fruit.
-rw-r--r--lib/rack.rb10
-rw-r--r--lib/rack/auth/abstract/handler.rb8
-rw-r--r--lib/rack/auth/digest/request.rb2
-rw-r--r--lib/rack/body_proxy.rb11
-rw-r--r--lib/rack/cascade.rb2
-rw-r--r--lib/rack/chunked.rb4
-rw-r--r--lib/rack/commonlogger.rb8
-rw-r--r--lib/rack/conditionalget.rb8
-rw-r--r--lib/rack/content_length.rb4
-rw-r--r--lib/rack/content_type.rb2
-rw-r--r--lib/rack/deflater.rb8
-rw-r--r--lib/rack/directory.rb10
-rw-r--r--lib/rack/etag.rb13
-rw-r--r--lib/rack/file.rb16
-rw-r--r--lib/rack/handler.rb2
-rw-r--r--lib/rack/handler/cgi.rb2
-rw-r--r--lib/rack/handler/fastcgi.rb2
-rw-r--r--lib/rack/handler/lsws.rb2
-rw-r--r--lib/rack/handler/mongrel.rb2
-rw-r--r--lib/rack/handler/scgi.rb4
-rw-r--r--lib/rack/handler/webrick.rb8
-rw-r--r--lib/rack/head.rb2
-rw-r--r--lib/rack/lint.rb22
-rw-r--r--lib/rack/lobster.rb4
-rw-r--r--lib/rack/methodoverride.rb6
-rw-r--r--lib/rack/mock.rb14
-rw-r--r--lib/rack/recursive.rb11
-rw-r--r--lib/rack/request.rb12
-rw-r--r--lib/rack/response.rb14
-rw-r--r--lib/rack/runtime.rb3
-rw-r--r--lib/rack/sendfile.rb4
-rw-r--r--lib/rack/server.rb2
-rw-r--r--lib/rack/showexceptions.rb4
-rw-r--r--lib/rack/showstatus.rb6
-rw-r--r--lib/rack/static.rb2
-rw-r--r--lib/rack/urlmap.rb4
36 files changed, 129 insertions, 109 deletions
diff --git a/lib/rack.rb b/lib/rack.rb
index 341514c5..97817bcb 100644
--- a/lib/rack.rb
+++ b/lib/rack.rb
@@ -22,6 +22,16 @@ module Rack
   def self.release
     "1.5"
   end
+  PATH_INFO      = 'PATH_INFO'.freeze
+  REQUEST_METHOD = 'REQUEST_METHOD'.freeze
+  SCRIPT_NAME    = 'SCRIPT_NAME'.freeze
+  QUERY_STRING   = 'QUERY_STRING'.freeze
+  CACHE_CONTROL  = 'Cache-Control'.freeze
+  CONTENT_LENGTH = 'Content-Length'.freeze
+  CONTENT_TYPE   = 'Content-Type'.freeze
+
+  GET  = 'GET'.freeze
+  HEAD = 'HEAD'.freeze
 
   autoload :Builder, "rack/builder"
   autoload :BodyProxy, "rack/body_proxy"
diff --git a/lib/rack/auth/abstract/handler.rb b/lib/rack/auth/abstract/handler.rb
index 214df629..c657691e 100644
--- a/lib/rack/auth/abstract/handler.rb
+++ b/lib/rack/auth/abstract/handler.rb
@@ -17,8 +17,8 @@ module Rack
 
       def unauthorized(www_authenticate = challenge)
         return [ 401,
-          { 'Content-Type' => 'text/plain',
-            'Content-Length' => '0',
+          { CONTENT_TYPE => 'text/plain',
+            CONTENT_LENGTH => '0',
             'WWW-Authenticate' => www_authenticate.to_s },
           []
         ]
@@ -26,8 +26,8 @@ module Rack
 
       def bad_request
         return [ 400,
-          { 'Content-Type' => 'text/plain',
-            'Content-Length' => '0' },
+          { CONTENT_TYPE => 'text/plain',
+            CONTENT_LENGTH => '0' },
           []
         ]
       end
diff --git a/lib/rack/auth/digest/request.rb b/lib/rack/auth/digest/request.rb
index 706c651c..01994364 100644
--- a/lib/rack/auth/digest/request.rb
+++ b/lib/rack/auth/digest/request.rb
@@ -7,7 +7,7 @@ module Rack
     module Digest
       class Request < Auth::AbstractRequest
         def method
-          @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD']
+          @env['rack.methodoverride.original_method'] || @env[REQUEST_METHOD]
         end
 
         def digest?
diff --git a/lib/rack/body_proxy.rb b/lib/rack/body_proxy.rb
index 95a74626..b35167c8 100644
--- a/lib/rack/body_proxy.rb
+++ b/lib/rack/body_proxy.rb
@@ -4,9 +4,14 @@ module Rack
       @body, @block, @closed = body, block, false
     end
 
-    def respond_to?(*args)
-      return false if args.first.to_s =~ /^to_ary$/
-      super or @body.respond_to?(*args)
+    def respond_to?(method_name)
+      case method_name
+      when :to_ary
+        return false
+      when String
+        return false if /^to_ary$/ =~ method_name
+      end
+      super or @body.respond_to?(method_name)
     end
 
     def close
diff --git a/lib/rack/cascade.rb b/lib/rack/cascade.rb
index c2891e5f..6b8f415a 100644
--- a/lib/rack/cascade.rb
+++ b/lib/rack/cascade.rb
@@ -4,7 +4,7 @@ module Rack
   # status codes).
 
   class Cascade
-    NotFound = [404, {"Content-Type" => "text/plain"}, []]
+    NotFound = [404, {CONTENT_TYPE => "text/plain"}, []]
 
     attr_reader :apps
 
diff --git a/lib/rack/chunked.rb b/lib/rack/chunked.rb
index ea221fa9..36c4959d 100644
--- a/lib/rack/chunked.rb
+++ b/lib/rack/chunked.rb
@@ -56,11 +56,11 @@ module Rack
 
       if ! chunkable_version?(env['HTTP_VERSION']) ||
          STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
-         headers['Content-Length'] ||
+         headers[CONTENT_LENGTH] ||
          headers['Transfer-Encoding']
         [status, headers, body]
       else
-        headers.delete('Content-Length')
+        headers.delete(CONTENT_LENGTH)
         headers['Transfer-Encoding'] = 'chunked'
         [status, headers, Body.new(body)]
       end
diff --git a/lib/rack/commonlogger.rb b/lib/rack/commonlogger.rb
index 1c99045e..d2d6dc34 100644
--- a/lib/rack/commonlogger.rb
+++ b/lib/rack/commonlogger.rb
@@ -46,9 +46,9 @@ module Rack
         env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
         env["REMOTE_USER"] || "-",
         now.strftime("%d/%b/%Y:%H:%M:%S %z"),
-        env["REQUEST_METHOD"],
-        env["PATH_INFO"],
-        env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
+        env[REQUEST_METHOD],
+        env[PATH_INFO],
+        env[QUERY_STRING].empty? ? "" : "?"+env[QUERY_STRING],
         env["HTTP_VERSION"],
         status.to_s[0..3],
         length,
@@ -65,7 +65,7 @@ module Rack
     end
 
     def extract_content_length(headers)
-      value = headers['Content-Length'] or return '-'
+      value = headers[CONTENT_LENGTH] or return '-'
       value.to_s == '0' ? '-' : value
     end
   end
diff --git a/lib/rack/conditionalget.rb b/lib/rack/conditionalget.rb
index 88573166..3d4c78aa 100644
--- a/lib/rack/conditionalget.rb
+++ b/lib/rack/conditionalget.rb
@@ -20,14 +20,14 @@ module Rack
     end
 
     def call(env)
-      case env['REQUEST_METHOD']
-      when "GET", "HEAD"
+      case env[REQUEST_METHOD]
+      when GET, HEAD
         status, headers, body = @app.call(env)
         headers = Utils::HeaderHash.new(headers)
         if status == 200 && fresh?(env, headers)
           status = 304
-          headers.delete('Content-Type')
-          headers.delete('Content-Length')
+          headers.delete(CONTENT_TYPE)
+          headers.delete(CONTENT_LENGTH)
           original_body = body
           body = Rack::BodyProxy.new([]) do
             original_body.close if original_body.respond_to?(:close)
diff --git a/lib/rack/content_length.rb b/lib/rack/content_length.rb
index 71bc919b..d0f491d1 100644
--- a/lib/rack/content_length.rb
+++ b/lib/rack/content_length.rb
@@ -16,7 +16,7 @@ module Rack
       headers = HeaderHash.new(headers)
 
       if !STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
-         !headers['Content-Length'] &&
+         !headers[CONTENT_LENGTH] &&
          !headers['Transfer-Encoding'] &&
          body.respond_to?(:to_ary)
 
@@ -28,7 +28,7 @@ module Rack
           obody.close if obody.respond_to?(:close)
         end
 
-        headers['Content-Length'] = length.to_s
+        headers[CONTENT_LENGTH] = length.to_s
       end
 
       [status, headers, body]
diff --git a/lib/rack/content_type.rb b/lib/rack/content_type.rb
index dd96e959..78ba43b7 100644
--- a/lib/rack/content_type.rb
+++ b/lib/rack/content_type.rb
@@ -20,7 +20,7 @@ module Rack
       headers = Utils::HeaderHash.new(headers)
 
       unless STATUS_WITH_NO_ENTITY_BODY.include?(status)
-        headers['Content-Type'] ||= @content_type
+        headers[CONTENT_TYPE] ||= @content_type
       end
 
       [status, headers, body]
diff --git a/lib/rack/deflater.rb b/lib/rack/deflater.rb
index 9df510bd..1788463b 100644
--- a/lib/rack/deflater.rb
+++ b/lib/rack/deflater.rb
@@ -54,20 +54,20 @@ module Rack
       case encoding
       when "gzip"
         headers['Content-Encoding'] = "gzip"
-        headers.delete('Content-Length')
+        headers.delete(CONTENT_LENGTH)
         mtime = headers.key?("Last-Modified") ?
           Time.httpdate(headers["Last-Modified"]) : Time.now
         [status, headers, GzipStream.new(body, mtime)]
       when "deflate"
         headers['Content-Encoding'] = "deflate"
-        headers.delete('Content-Length')
+        headers.delete(CONTENT_LENGTH)
         [status, headers, DeflateStream.new(body)]
       when "identity"
         [status, headers, body]
       when nil
         message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
         bp = Rack::BodyProxy.new([message]) { body.close if body.respond_to?(:close) }
-        [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, bp]
+        [406, {CONTENT_TYPE => "text/plain", CONTENT_LENGTH => message.length.to_s}, bp]
       end
     end
 
@@ -138,7 +138,7 @@ module Rack
       # Skip compressing empty entity body responses and responses with
       # no-transform set.
       if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
-          headers['Cache-Control'].to_s =~ /\bno-transform\b/ ||
+          headers[CACHE_CONTROL].to_s =~ /\bno-transform\b/ ||
          (headers['Content-Encoding'] && headers['Content-Encoding'] !~ /\bidentity\b/)
         return false
       end
diff --git a/lib/rack/directory.rb b/lib/rack/directory.rb
index 602f2d44..98d66e02 100644
--- a/lib/rack/directory.rb
+++ b/lib/rack/directory.rb
@@ -55,8 +55,8 @@ table { width:100%%; }
 
     def _call(env)
       @env = env
-      @script_name = env['SCRIPT_NAME']
-      @path_info = Utils.unescape(env['PATH_INFO'])
+      @script_name = env[SCRIPT_NAME]
+      @path_info = Utils.unescape(env[PATH_INFO])
 
       if forbidden = check_forbidden
         forbidden
@@ -72,7 +72,7 @@ table { width:100%%; }
       body = "Forbidden\n"
       size = Rack::Utils.bytesize(body)
       return [403, {"Content-Type" => "text/plain",
-        "Content-Length" => size.to_s,
+        CONTENT_LENGTH => size.to_s,
         "X-Cascade" => "pass"}, [body]]
     end
 
@@ -101,7 +101,7 @@ table { width:100%%; }
         @files << [ url, basename, size, type, mtime ]
       end
 
-      return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ]
+      return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'}, self ]
     end
 
     def stat(node, max = 10)
@@ -130,7 +130,7 @@ table { width:100%%; }
       body = "Entity not found: #{@path_info}\n"
       size = Rack::Utils.bytesize(body)
       return [404, {"Content-Type" => "text/plain",
-        "Content-Length" => size.to_s,
+        CONTENT_LENGTH => size.to_s,
         "X-Cascade" => "pass"}, [body]]
     end
 
diff --git a/lib/rack/etag.rb b/lib/rack/etag.rb
index fefe671f..88973131 100644
--- a/lib/rack/etag.rb
+++ b/lib/rack/etag.rb
@@ -11,6 +11,7 @@ module Rack
   # used when Etag is absent and a directive when it is present. The first
   # defaults to nil, while the second defaults to "max-age=0, private, must-revalidate"
   class ETag
+    ETAG_STRING = 'ETag'.freeze
     DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
 
     def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTROL)
@@ -28,14 +29,14 @@ module Rack
         body = Rack::BodyProxy.new(new_body) do
           original_body.close if original_body.respond_to?(:close)
         end
-        headers['ETag'] = %(W/"#{digest}") if digest
+        headers[ETAG_STRING] = %(W/"#{digest}") if digest
       end
 
-      unless headers['Cache-Control']
+      unless headers[CACHE_CONTROL]
         if digest
-          headers['Cache-Control'] = @cache_control if @cache_control
+          headers[CACHE_CONTROL] = @cache_control if @cache_control
         else
-          headers['Cache-Control'] = @no_cache_control if @no_cache_control
+          headers[CACHE_CONTROL] = @no_cache_control if @no_cache_control
         end
       end
 
@@ -53,8 +54,8 @@ module Rack
       end
 
       def skip_caching?(headers)
-        (headers['Cache-Control'] && headers['Cache-Control'].include?('no-cache')) ||
-          headers.key?('ETag') || headers.key?('Last-Modified')
+        (headers[CACHE_CONTROL] && headers[CACHE_CONTROL].include?('no-cache')) ||
+          headers.key?(ETAG_STRING) || headers.key?('Last-Modified')
       end
 
       def digest_body(body)
diff --git a/lib/rack/file.rb b/lib/rack/file.rb
index c8f8d0d1..bdae6a66 100644
--- a/lib/rack/file.rb
+++ b/lib/rack/file.rb
@@ -34,11 +34,11 @@ module Rack
     F = ::File
 
     def _call(env)
-      unless ALLOWED_VERBS.include? env["REQUEST_METHOD"]
+      unless ALLOWED_VERBS.include? env[REQUEST_METHOD]
         return fail(405, "Method Not Allowed", {'Allow' => ALLOW_HEADER})
       end
 
-      path_info = Utils.unescape(env["PATH_INFO"])
+      path_info = Utils.unescape(env[PATH_INFO])
       clean_path_info = Utils.clean_path_info(path_info)
 
       @path = F.join(@root, clean_path_info)
@@ -58,19 +58,19 @@ module Rack
 
     def serving(env)
       if env["REQUEST_METHOD"] == "OPTIONS"
-              return [200, {'Allow' => ALLOW_HEADER, 'Content-Length' => '0'}, []]
+              return [200, {'Allow' => ALLOW_HEADER, CONTENT_LENGTH => '0'}, []]
       end
       last_modified = F.mtime(@path).httpdate
       return [304, {}, []] if env['HTTP_IF_MODIFIED_SINCE'] == last_modified
 
       headers = { "Last-Modified" => last_modified }
       mime = Mime.mime_type(F.extname(@path), @default_mime)
-      headers["Content-Type"] = mime if mime
+      headers[CONTENT_TYPE] = mime if mime
 
       # Set custom headers
       @headers.each { |field, content| headers[field] = content } if @headers
 
-      response = [ 200, headers, env["REQUEST_METHOD"] == "HEAD" ? [] : self ]
+      response = [ 200, headers, env[REQUEST_METHOD] == "HEAD" ? [] : self ]
 
       # NOTE:
       #   We check via File::size? whether this file provides size info
@@ -97,7 +97,7 @@ module Rack
         size = @range.end - @range.begin + 1
       end
 
-      response[1]["Content-Length"] = size.to_s
+      response[1][CONTENT_LENGTH] = size.to_s
       response
     end
 
@@ -122,8 +122,8 @@ module Rack
       [
         status,
         {
-          "Content-Type" => "text/plain",
-          "Content-Length" => body.size.to_s,
+          CONTENT_TYPE   => "text/plain",
+          CONTENT_LENGTH => body.size.to_s,
           "X-Cascade" => "pass"
         }.merge!(headers),
         [body]
diff --git a/lib/rack/handler.rb b/lib/rack/handler.rb
index b5fa0268..f11e37e3 100644
--- a/lib/rack/handler.rb
+++ b/lib/rack/handler.rb
@@ -51,7 +51,7 @@ module Rack
         options.delete :Port
 
         Rack::Handler::FastCGI
-      elsif ENV.include?("REQUEST_METHOD")
+      elsif ENV.include?(REQUEST_METHOD)
         Rack::Handler::CGI
       elsif ENV.include?("RACK_HANDLER")
         self.get(ENV["RACK_HANDLER"])
diff --git a/lib/rack/handler/cgi.rb b/lib/rack/handler/cgi.rb
index 15af1ac2..78e135e9 100644
--- a/lib/rack/handler/cgi.rb
+++ b/lib/rack/handler/cgi.rb
@@ -26,7 +26,7 @@ module Rack
                      "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
                    })
 
-        env["QUERY_STRING"] ||= ""
+        env[QUERY_STRING]   ||= ""
         env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
         env["REQUEST_PATH"] ||= "/"
 
diff --git a/lib/rack/handler/fastcgi.rb b/lib/rack/handler/fastcgi.rb
index b26fabc3..5137992b 100644
--- a/lib/rack/handler/fastcgi.rb
+++ b/lib/rack/handler/fastcgi.rb
@@ -59,7 +59,7 @@ module Rack
                      "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
                    })
 
-        env["QUERY_STRING"] ||= ""
+        env[QUERY_STRING]   ||= ""
         env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
         env["REQUEST_PATH"] ||= "/"
         env.delete "CONTENT_TYPE"  if env["CONTENT_TYPE"] == ""
diff --git a/lib/rack/handler/lsws.rb b/lib/rack/handler/lsws.rb
index 1dee78a3..aec27322 100644
--- a/lib/rack/handler/lsws.rb
+++ b/lib/rack/handler/lsws.rb
@@ -27,7 +27,7 @@ module Rack
           "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
         )
 
-        env["QUERY_STRING"] ||= ""
+        env[QUERY_STRING]   ||= ""
         env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
         env["REQUEST_PATH"] ||= "/"
         status, headers, body = app.call(env)
diff --git a/lib/rack/handler/mongrel.rb b/lib/rack/handler/mongrel.rb
index 20be86b1..ab9891b1 100644
--- a/lib/rack/handler/mongrel.rb
+++ b/lib/rack/handler/mongrel.rb
@@ -78,7 +78,7 @@ module Rack
 
                      "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
                    })
-        env["QUERY_STRING"] ||= ""
+        env[QUERY_STRING] ||= ""
 
         status, headers, body = @app.call(env)
 
diff --git a/lib/rack/handler/scgi.rb b/lib/rack/handler/scgi.rb
index 40e86fb9..9a465eae 100644
--- a/lib/rack/handler/scgi.rb
+++ b/lib/rack/handler/scgi.rb
@@ -35,9 +35,9 @@ module Rack
         env = Hash[request]
         env.delete "HTTP_CONTENT_TYPE"
         env.delete "HTTP_CONTENT_LENGTH"
-        env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2)
+        env["REQUEST_PATH"], env[QUERY_STRING] = env["REQUEST_URI"].split('?', 2)
         env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
-        env["PATH_INFO"] = env["REQUEST_PATH"]
+        env[PATH_INFO] = env["REQUEST_PATH"]
         env["QUERY_STRING"] ||= ""
         env["SCRIPT_NAME"] = ""
 
diff --git a/lib/rack/handler/webrick.rb b/lib/rack/handler/webrick.rb
index 023d8b27..dcbf96ca 100644
--- a/lib/rack/handler/webrick.rb
+++ b/lib/rack/handler/webrick.rb
@@ -79,12 +79,12 @@ module Rack
                    })
 
         env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
-        env["QUERY_STRING"] ||= ""
-        unless env["PATH_INFO"] == ""
+        env[QUERY_STRING] ||= ""
+        unless env[PATH_INFO] == ""
           path, n = req.request_uri.path, env["SCRIPT_NAME"].length
-          env["PATH_INFO"] = path[n, path.length-n]
+          env[PATH_INFO] = path[n, path.length-n]
         end
-        env["REQUEST_PATH"] ||= [env["SCRIPT_NAME"], env["PATH_INFO"]].join
+        env["REQUEST_PATH"] ||= [env["SCRIPT_NAME"], env[PATH_INFO]].join
 
         status, headers, body = @app.call(env)
         begin
diff --git a/lib/rack/head.rb b/lib/rack/head.rb
index 72f3dbdd..f487254a 100644
--- a/lib/rack/head.rb
+++ b/lib/rack/head.rb
@@ -12,7 +12,7 @@ class Head
   def call(env)
     status, headers, body = @app.call(env)
 
-    if env["REQUEST_METHOD"] == "HEAD"
+    if env[REQUEST_METHOD] == HEAD
       [
         status, headers, Rack::BodyProxy.new([]) do
           body.close if body.respond_to? :close
diff --git a/lib/rack/lint.rb b/lib/rack/lint.rb
index 667c34a6..5cdef3f8 100644
--- a/lib/rack/lint.rb
+++ b/lib/rack/lint.rb
@@ -57,7 +57,7 @@ module Rack
       ## and the *body*.
       check_content_type status, headers
       check_content_length status, headers
-      @head_request = env["REQUEST_METHOD"] == "HEAD"
+      @head_request = env[REQUEST_METHOD] == "HEAD"
       [status, headers, self]
     end
 
@@ -278,7 +278,7 @@ module Rack
       check_hijack env
 
       ## * The <tt>REQUEST_METHOD</tt> must be a valid token.
-      assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") {
+      assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD]}") {
         env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
       }
 
@@ -510,20 +510,20 @@ module Rack
           ## already present, in rack.hijack_io.
           io = original_hijack.call
           HijackWrapper.new(io)
-          ##
+          ##
           ## rack.hijack_io must respond to:
           ## <tt>read, write, read_nonblock, write_nonblock, flush, close,
           ## close_read, close_write, closed?</tt>
-          ##
+          ##
           ## The semantics of these IO methods must be a best effort match to
           ## those of a normal ruby IO or Socket object, using standard
           ## arguments and raising standard exceptions. Servers are encouraged
           ## to simply pass on real IO objects, although it is recognized that
           ## this approach is not directly compatible with SPDY and HTTP 2.0.
-          ##
+          ##
           ## IO provided in rack.hijack_io should preference the
           ## IO::WaitReadable and IO::WaitWritable APIs wherever supported.
-          ##
+          ##
           ## There is a deliberate lack of full specification around
           ## rack.hijack_io, as semantics will change from server to server.
           ## Users are encouraged to utilize this API with a knowledge of their
@@ -535,10 +535,10 @@ module Rack
           io
         end
       else
-        ##
+        ##
         ## If rack.hijack? is false, then rack.hijack should not be set.
         assert("rack.hijack? is false, but rack.hijack is present") { env['rack.hijack'].nil? }
-        ##
+        ##
         ## If rack.hijack? is false, then rack.hijack_io should not be set.
         assert("rack.hijack? is false, but rack.hijack_io is present") { env['rack.hijack_io'].nil? }
       end
@@ -557,7 +557,7 @@ module Rack
       ## <tt>rack.hijack</tt> to an object that responds to <tt>call</tt>
       ## accepting an argument that conforms to the <tt>rack.hijack_io</tt>
       ## protocol.
-      ##
+      ##
       ## After the headers have been sent, and this hijack callback has been
       ## called, the application is now responsible for the remaining lifecycle
       ## of the IO. The application is also responsible for maintaining HTTP
@@ -566,7 +566,7 @@ module Rack
       ## HTTP/1.1, and not Connection:keep-alive, as there is no protocol for
       ## returning hijacked sockets to the web server. For that purpose, use the
       ## body streaming API instead (progressively yielding strings via each).
-      ##
+      ##
       ## Servers must ignore the <tt>body</tt> part of the response tuple when
       ## the <tt>rack.hijack</tt> response API is in use.
 
@@ -579,7 +579,7 @@ module Rack
           original_hijack.call HijackWrapper.new(io)
         end
       else
-        ##
+        ##
         ## The special response header <tt>rack.hijack</tt> must only be set
         ## if the request env has <tt>rack.hijack?</tt> <tt>true</tt>.
         assert('rack.hijack header must not be present if server does not support hijacking') {
diff --git a/lib/rack/lobster.rb b/lib/rack/lobster.rb
index 195bd945..b7d15278 100644
--- a/lib/rack/lobster.rb
+++ b/lib/rack/lobster.rb
@@ -12,7 +12,7 @@ module Rack
     I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0])
 
     LambdaLobster = lambda { |env|
-      if env["QUERY_STRING"].include?("flip")
+      if env[QUERY_STRING].include?("flip")
         lobster = LobsterString.split("\n").
           map { |line| line.ljust(42).reverse }.
           join("\n")
@@ -26,7 +26,7 @@ module Rack
                  "<pre>", lobster, "</pre>",
                  "<a href='#{href}'>flip!</a>"]
       length = content.inject(0) { |a,e| a+e.size }.to_s
-      [200, {"Content-Type" => "text/html", "Content-Length" => length}, content]
+      [200, {CONTENT_TYPE => "text/html", CONTENT_LENGTH => length}, content]
     }
 
     def call(env)
diff --git a/lib/rack/methodoverride.rb b/lib/rack/methodoverride.rb
index 062f3d67..7d2b56eb 100644
--- a/lib/rack/methodoverride.rb
+++ b/lib/rack/methodoverride.rb
@@ -11,11 +11,11 @@ module Rack
     end
 
     def call(env)
-      if allowed_methods.include?(env["REQUEST_METHOD"])
+      if allowed_methods.include?(env[REQUEST_METHOD])
         method = method_override(env)
         if HTTP_METHODS.include?(method)
-          env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
-          env["REQUEST_METHOD"] = method
+          env["rack.methodoverride.original_method"] = env[REQUEST_METHOD]
+          env[REQUEST_METHOD] = method
         end
       end
 
diff --git a/lib/rack/mock.rb b/lib/rack/mock.rb
index 3c02c1fe..217ae0f7 100644
--- a/lib/rack/mock.rb
+++ b/lib/rack/mock.rb
@@ -91,15 +91,15 @@ module Rack
 
       env = DEFAULT_ENV.dup
 
-      env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
+      env[REQUEST_METHOD] = opts[:method] ? opts[:method].to_s.upcase : "GET"
       env["SERVER_NAME"] = uri.host || "example.org"
       env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
-      env["QUERY_STRING"] = uri.query.to_s
-      env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
+      env[QUERY_STRING] = uri.query.to_s
+      env[PATH_INFO] = (!uri.path || uri.path.empty?) ? "/" : uri.path
       env["rack.url_scheme"] = uri.scheme || "http"
       env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
 
-      env["SCRIPT_NAME"] = opts[:script_name] || ""
+      env[SCRIPT_NAME] = opts[:script_name] || ""
 
       if opts[:fatal]
         env["rack.errors"] = FatalWarner.new
@@ -108,10 +108,10 @@ module Rack
       end
 
       if params = opts[:params]
-        if env["REQUEST_METHOD"] == "GET"
+        if env[REQUEST_METHOD] == "GET"
           params = Utils.parse_nested_query(params) if params.is_a?(String)
-          params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
-          env["QUERY_STRING"] = Utils.build_nested_query(params)
+          params.update(Utils.parse_nested_query(env[QUERY_STRING]))
+          env[QUERY_STRING] = Utils.build_nested_query(params)
         elsif !opts.has_key?(:input)
           opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
           if params.is_a?(Hash)
diff --git a/lib/rack/recursive.rb b/lib/rack/recursive.rb
index b431d4dd..17d17dfd 100644
--- a/lib/rack/recursive.rb
+++ b/lib/rack/recursive.rb
@@ -14,8 +14,8 @@ module Rack
       @url = URI(url)
       @env = env
 
-      @env["PATH_INFO"] =       @url.path
-      @env["QUERY_STRING"] =    @url.query  if @url.query
+      @env[PATH_INFO] =         @url.path
+      @env[QUERY_STRING] =      @url.query  if @url.query
       @env["HTTP_HOST"] =       @url.host   if @url.host
       @env["HTTP_PORT"] =       @url.port   if @url.port
       @env["rack.url_scheme"] = @url.scheme if @url.scheme
@@ -39,7 +39,7 @@ module Rack
     end
 
     def _call(env)
-      @script_name = env["SCRIPT_NAME"]
+      @script_name = env[SCRIPT_NAME]
       @app.call(env.merge('rack.recursive.include' => method(:include)))
     rescue ForwardRequest => req
       call(env.merge(req.env))
@@ -51,8 +51,9 @@ module Rack
         raise ArgumentError, "can only include below #{@script_name}, not #{path}"
       end
 
-      env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name,
-                      "REQUEST_METHOD" => "GET",
+      env = env.merge(PATH_INFO => path,
+                      SCRIPT_NAME => @script_name,
+                      REQUEST_METHOD => "GET",
                       "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "",
                       "rack.input" => StringIO.new(""))
       @app.call(env)
diff --git a/lib/rack/request.rb b/lib/rack/request.rb
index 602d0d64..e6e46e9d 100644
--- a/lib/rack/request.rb
+++ b/lib/rack/request.rb
@@ -18,10 +18,10 @@ module Rack
     end
 
     def body;            @env["rack.input"]                       end
-    def script_name;     @env["SCRIPT_NAME"].to_s                 end
-    def path_info;       @env["PATH_INFO"].to_s                   end
+    def script_name;     @env[SCRIPT_NAME].to_s                   end
+    def path_info;       @env[PATH_INFO].to_s                     end
     def request_method;  @env["REQUEST_METHOD"]                   end
-    def query_string;    @env["QUERY_STRING"].to_s                end
+    def query_string;    @env[QUERY_STRING].to_s                  end
     def content_length;  @env['CONTENT_LENGTH']                   end
 
     def content_type
@@ -116,10 +116,10 @@ module Rack
     def delete?;  request_method == "DELETE"  end
 
     # Checks the HTTP request method (or verb) to see if it was of type GET
-    def get?;     request_method == "GET"     end
+    def get?;     request_method == GET       end
 
     # Checks the HTTP request method (or verb) to see if it was of type HEAD
-    def head?;    request_method == "HEAD"    end
+    def head?;    request_method == HEAD      end
 
     # Checks the HTTP request method (or verb) to see if it was of type OPTIONS
     def options?; request_method == "OPTIONS" end
@@ -173,7 +173,7 @@ module Rack
     # Content-Type header is provided and the request_method is POST.
     def form_data?
       type = media_type
-      meth = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
+      meth = env["rack.methodoverride.original_method"] || env[REQUEST_METHOD]
       (meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
     end
 
diff --git a/lib/rack/response.rb b/lib/rack/response.rb
index 12536710..7f0c6b71 100644
--- a/lib/rack/response.rb
+++ b/lib/rack/response.rb
@@ -20,11 +20,13 @@ module Rack
   class Response
     attr_accessor :length
 
+    CHUNKED = 'chunked'.freeze
+    TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
     def initialize(body=[], status=200, header={})
       @status = status.to_i
       @header = Utils::HeaderHash.new.merge(header)
 
-      @chunked = "chunked" == @header['Transfer-Encoding']
+      @chunked = CHUNKED == @header[TRANSFER_ENCODING]
       @writer  = lambda { |x| @body << x }
       @block   = nil
       @length  = 0
@@ -72,8 +74,8 @@ module Rack
       @block = block
 
       if [204, 205, 304].include?(status.to_i)
-        header.delete "Content-Type"
-        header.delete "Content-Length"
+        header.delete CONTENT_TYPE
+        header.delete CONTENT_LENGTH
         close
         [status.to_i, header, []]
       else
@@ -98,7 +100,7 @@ module Rack
       @length += Rack::Utils.bytesize(s) unless @chunked
       @writer.call s
 
-      header["Content-Length"] = @length.to_s unless @chunked
+      header[CONTENT_LENGTH] = @length.to_s unless @chunked
       str
     end
 
@@ -142,11 +144,11 @@ module Rack
       end
 
       def content_type
-        headers["Content-Type"]
+        headers[CONTENT_TYPE]
       end
 
       def content_length
-        cl = headers["Content-Length"]
+        cl = headers[CONTENT_LENGTH]
         cl ? cl.to_i : cl
       end
 
diff --git a/lib/rack/runtime.rb b/lib/rack/runtime.rb
index 1bd411fd..83d6c0ca 100644
--- a/lib/rack/runtime.rb
+++ b/lib/rack/runtime.rb
@@ -12,13 +12,14 @@ module Rack
       @header_name << "-#{name}" if name
     end
 
+    FORMAT_STRING = "%0.6f"
     def call(env)
       start_time = Time.now
       status, headers, body = @app.call(env)
       request_time = Time.now - start_time
 
       if !headers.has_key?(@header_name)
-        headers[@header_name] = "%0.6f" % request_time
+        headers[@header_name] = FORMAT_STRING % request_time
       end
 
       [status, headers, body]
diff --git a/lib/rack/sendfile.rb b/lib/rack/sendfile.rb
index 8e2c3d67..4a9b428b 100644
--- a/lib/rack/sendfile.rb
+++ b/lib/rack/sendfile.rb
@@ -116,7 +116,7 @@ module Rack
         when 'X-Accel-Redirect'
           path = F.expand_path(body.to_path)
           if url = map_accel_path(env, path)
-            headers['Content-Length'] = '0'
+            headers[CONTENT_LENGTH] = '0'
             headers[type] = url
             obody = body
             body = Rack::BodyProxy.new([]) do
@@ -127,7 +127,7 @@ module Rack
           end
         when 'X-Sendfile', 'X-Lighttpd-Send-File'
           path = F.expand_path(body.to_path)
-          headers['Content-Length'] = '0'
+          headers[CONTENT_LENGTH] = '0'
           headers[type] = path
           obody = body
           body = Rack::BodyProxy.new([]) do
diff --git a/lib/rack/server.rb b/lib/rack/server.rb
index d9f971d7..ea1ad82b 100644
--- a/lib/rack/server.rb
+++ b/lib/rack/server.rb
@@ -310,7 +310,7 @@ module Rack
 
         # Don't evaluate CGI ISINDEX parameters.
         # http://www.meb.uni-bonn.de/docs/cgi/cl.html
-        args.clear if ENV.include?("REQUEST_METHOD")
+        args.clear if ENV.include?(REQUEST_METHOD)
 
         options.merge! opt_parser.parse!(args)
         options[:config] = ::File.expand_path(options[:config])
diff --git a/lib/rack/showexceptions.rb b/lib/rack/showexceptions.rb
index 731aea49..60999e64 100644
--- a/lib/rack/showexceptions.rb
+++ b/lib/rack/showexceptions.rb
@@ -39,8 +39,8 @@ module Rack
       [
         500,
         {
-          "Content-Type" => content_type,
-          "Content-Length" => Rack::Utils.bytesize(body).to_s,
+          CONTENT_TYPE => content_type,
+          CONTENT_LENGTH => Rack::Utils.bytesize(body).to_s,
         },
         [body],
       ]
diff --git a/lib/rack/showstatus.rb b/lib/rack/showstatus.rb
index 6892a5b7..4426310a 100644
--- a/lib/rack/showstatus.rb
+++ b/lib/rack/showstatus.rb
@@ -3,7 +3,7 @@ require 'rack/request'
 require 'rack/utils'
 
 module Rack
-  # Rack::ShowStatus catches all empty responses and replaces them
+  # Rack::ShowStatus catches all empty responses and replaces them
   # with a site explaining the error.
   #
   # Additional details can be put into <tt>rack.showstatus.detail</tt>
@@ -19,7 +19,7 @@ module Rack
     def call(env)
       status, headers, body = @app.call(env)
       headers = Utils::HeaderHash.new(headers)
-      empty = headers['Content-Length'].to_i <= 0
+      empty = headers[CONTENT_LENGTH].to_i <= 0
 
       # client or server error, or explicit message
       if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"]
@@ -35,7 +35,7 @@ module Rack
 
         body = @template.result(binding)
         size = Rack::Utils.bytesize(body)
-        [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]]
+        [status, headers.merge(CONTENT_TYPE => "text/html", CONTENT_LENGTH => size.to_s), [body]]
       else
         [status, headers, body]
       end
diff --git a/lib/rack/static.rb b/lib/rack/static.rb
index 41aec7f3..75e1e555 100644
--- a/lib/rack/static.rb
+++ b/lib/rack/static.rb
@@ -107,7 +107,7 @@ module Rack
     end
 
     def call(env)
-      path = env["PATH_INFO"]
+      path = env[PATH_INFO]
 
       if can_serve(path)
         env["PATH_INFO"] = (path =~ /\/$/ ? path + @index : @urls[path]) if overwrite_file_path(path)
diff --git a/lib/rack/urlmap.rb b/lib/rack/urlmap.rb
index d75f5a22..c62baecc 100644
--- a/lib/rack/urlmap.rb
+++ b/lib/rack/urlmap.rb
@@ -41,7 +41,7 @@ module Rack
     end
 
     def call(env)
-      path = env['PATH_INFO']
+      path = env[PATH_INFO]
       script_name = env['SCRIPT_NAME']
       hHost = env['HTTP_HOST']
       sName = env['SERVER_NAME']
@@ -66,7 +66,7 @@ module Rack
         return app.call(env)
       end
 
-      [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
+      [404, {CONTENT_TYPE => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
 
     ensure
       env['PATH_INFO'] = path