summary refs log tree commit
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2015-09-04 17:33:45 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2015-09-04 17:33:45 -0700
commitb2d73960e9ea6b8b15321ef190f13a290d1aedf0 (patch)
tree86ade50f1fbae3dbcb7b048bd3157f053dabd330
parent7f1fcde28785051886b9d88dc66db32c932d94d2 (diff)
downloadrack-b2d73960e9ea6b8b15321ef190f13a290d1aedf0.tar.gz
pull env access in the request object to a module
this also tests that delegation to the request object is possible.  I
want to see exactly how many methods we need to delegate in order to
look like a real request object
-rw-r--r--HISTORY.md6
-rw-r--r--lib/rack/mock.rb4
-rw-r--r--lib/rack/multipart.rb3
-rw-r--r--lib/rack/request.rb164
-rw-r--r--lib/rack/utils.rb3
-rw-r--r--test/spec_multipart.rb8
-rw-r--r--test/spec_request.rb329
7 files changed, 289 insertions, 228 deletions
diff --git a/HISTORY.md b/HISTORY.md
index 6079a5af..41887dda 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,3 +1,9 @@
+Fri Sep  4 17:32:12 2015  Aaron Patterson <tenderlove@ruby-lang.org>
+
+        * Pull `ENV` access inside the request object in to a module.  This
+        will help with legacy Request objects that are ENV based but don't
+        want to inherit from Rack::Request
+
 Fri Sep  4 16:09:11 2015  Aaron Patterson <tenderlove@ruby-lang.org>
 
         * Move most methods on the `Rack::Request` to a module
diff --git a/lib/rack/mock.rb b/lib/rack/mock.rb
index 0002bfe4..a2fc2401 100644
--- a/lib/rack/mock.rb
+++ b/lib/rack/mock.rb
@@ -115,10 +115,10 @@ module Rack
         elsif !opts.has_key?(:input)
           opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
           if params.is_a?(Hash)
-            if data = Utils::Multipart.build_multipart(params)
+            if data = Rack::Multipart.build_multipart(params)
               opts[:input] = data
               opts["CONTENT_LENGTH"] ||= data.length.to_s
-              opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
+              opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
             else
               opts[:input] = Utils.build_nested_query(params)
             end
diff --git a/lib/rack/multipart.rb b/lib/rack/multipart.rb
index 748fba00..12c3917b 100644
--- a/lib/rack/multipart.rb
+++ b/lib/rack/multipart.rb
@@ -1,10 +1,11 @@
+require 'rack/multipart/parser'
+
 module Rack
   # A multipart form data parser, adapted from IOWA.
   #
   # Usually, Rack::Request#POST takes care of calling this.
   module Multipart
     autoload :UploadedFile, 'rack/multipart/uploaded_file'
-    autoload :Parser, 'rack/multipart/parser'
     autoload :Generator, 'rack/multipart/generator'
 
     EOL = "\r\n"
diff --git a/lib/rack/request.rb b/lib/rack/request.rb
index c0c921d4..afa5d590 100644
--- a/lib/rack/request.rb
+++ b/lib/rack/request.rb
@@ -11,72 +11,11 @@ module Rack
   #   req.params["data"]
 
   class Request
-    HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'.freeze
-    HTTP_X_FORWARDED_PROTO  = 'HTTP_X_FORWARDED_PROTO'.freeze
-    HTTP_X_FORWARDED_HOST   = 'HTTP_X_FORWARDED_HOST'.freeze
-    HTTP_X_FORWARDED_PORT   = 'HTTP_X_FORWARDED_PORT'.freeze
-    HTTP_X_FORWARDED_SSL    = 'HTTP_X_FORWARDED_SSL'.freeze
-
-    # The environment of the request.
-    attr_reader :env
-
     def initialize(env)
-      @env = env
-      super()
-    end
-
-    # Get a request specific value for `name`.
-    def get_header(name)
-      @env[name]
-    end
-
-    # If a block is given, it yields to the block if the value hasn't been set
-    # on the request.
-    def fetch_header(name, &block)
-      @env.fetch(name, &block)
-    end
-
-    # Delete a request specific value for `name`.
-    def delete_header(name)
-      @env.delete name
-    end
-
-    # Set a request specific value for `name` to `v`
-    def set_header(name, v)
-      @env[name] = v
+      @params = nil
+      super(env)
     end
 
-    # Predicate method to test to see if `name` has been set as request
-    # specific data
-    def has_header?(name)
-      @env.key? name
-    end
-
-    # Loops through each key / value pair in the request specific data.
-    def each_header(&block)
-      @env.each(&block)
-    end
-
-    # The set of form-data media-types. Requests that do not indicate
-    # one of the media types presents in this list will not be eligible
-    # for form-data / param parsing.
-    FORM_DATA_MEDIA_TYPES = [
-      'application/x-www-form-urlencoded',
-      'multipart/form-data'
-    ]
-
-    # The set of media-types. Requests that do not indicate
-    # one of the media types presents in this list will not be eligible
-    # for param parsing like soap attachments or generic multiparts
-    PARSEABLE_DATA_MEDIA_TYPES = [
-      'multipart/related',
-      'multipart/mixed'
-    ]
-
-    # Default ports depending on scheme. Used to decide whether or not
-    # to include the port in a generated URI.
-    DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
-
     # shortcut for request.params[key]
     def [](key)
       params[key.to_s]
@@ -94,15 +33,94 @@ module Rack
       keys.map{|key| params[key] }
     end
 
-    def initialize_copy(other)
-      @env = other.env.dup
+    def params
+      @params ||= super
     end
 
-    module Helpers
-      def initialize
-        @params = nil
+    def update_param(k, v)
+      super
+      @params = nil
+    end
+
+    def delete_param(k)
+      v = super
+      @params = nil
+      v
+    end
+
+    module Env
+      # The environment of the request.
+      attr_reader :env
+
+      def initialize(env)
+        @env = env
+        super()
+      end
+
+      # Get a request specific value for `name`.
+      def get_header(name)
+        @env[name]
+      end
+
+      # If a block is given, it yields to the block if the value hasn't been set
+      # on the request.
+      def fetch_header(name, &block)
+        @env.fetch(name, &block)
+      end
+
+      # Delete a request specific value for `name`.
+      def delete_header(name)
+        @env.delete name
+      end
+
+      # Set a request specific value for `name` to `v`
+      def set_header(name, v)
+        @env[name] = v
       end
 
+      # Predicate method to test to see if `name` has been set as request
+      # specific data
+      def has_header?(name)
+        @env.key? name
+      end
+
+      # Loops through each key / value pair in the request specific data.
+      def each_header(&block)
+        @env.each(&block)
+      end
+
+      def initialize_copy(other)
+        @env = other.env.dup
+      end
+    end
+
+    module Helpers
+      # The set of form-data media-types. Requests that do not indicate
+      # one of the media types presents in this list will not be eligible
+      # for form-data / param parsing.
+      FORM_DATA_MEDIA_TYPES = [
+        'application/x-www-form-urlencoded',
+        'multipart/form-data'
+      ]
+
+      # The set of media-types. Requests that do not indicate
+      # one of the media types presents in this list will not be eligible
+      # for param parsing like soap attachments or generic multiparts
+      PARSEABLE_DATA_MEDIA_TYPES = [
+        'multipart/related',
+        'multipart/mixed'
+      ]
+
+      # Default ports depending on scheme. Used to decide whether or not
+      # to include the port in a generated URI.
+      DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
+
+      HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'.freeze
+      HTTP_X_FORWARDED_PROTO  = 'HTTP_X_FORWARDED_PROTO'.freeze
+      HTTP_X_FORWARDED_HOST   = 'HTTP_X_FORWARDED_HOST'.freeze
+      HTTP_X_FORWARDED_PORT   = 'HTTP_X_FORWARDED_PORT'.freeze
+      HTTP_X_FORWARDED_SSL    = 'HTTP_X_FORWARDED_SSL'.freeze
+
       def body;            get_header(RACK_INPUT)                         end
       def script_name;     get_header(SCRIPT_NAME).to_s                   end
       def script_name=(s); set_header(SCRIPT_NAME, s.to_s)                end
@@ -319,7 +337,7 @@ module Rack
 
             get_header(RACK_INPUT).rewind
           end
-          set_header RACK_REQUEST_FORM_INPUT, @env[RACK_INPUT]
+          set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
           get_header RACK_REQUEST_FORM_HASH
         else
           {}
@@ -330,7 +348,7 @@ module Rack
       #
       # Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
       def params
-        @params ||= self.GET.merge(self.POST)
+        self.GET.merge(self.POST)
       rescue EOFError
         self.GET.dup
       end
@@ -353,7 +371,6 @@ module Rack
         unless found
           self.GET[k] = v
         end
-        @params = nil
       end
 
       # Destructively delete a parameter, whether it's in GET or POST. Returns the value of the deleted parameter.
@@ -362,9 +379,7 @@ module Rack
       #
       # env['rack.input'] is not touched.
       def delete_param(k)
-        v = [ self.POST.delete(k), self.GET.delete(k) ].compact.first
-        @params = nil
-        v
+        [ self.POST.delete(k), self.GET.delete(k) ].compact.first
       end
 
       def base_url
@@ -432,6 +447,7 @@ module Rack
       end
     end
 
+    include Env
     include Helpers
   end
 end
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 05b50921..cc4d1ba8 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -2,7 +2,6 @@
 require 'fileutils'
 require 'set'
 require 'tempfile'
-require 'rack/multipart'
 require 'rack/query_parser'
 require 'time'
 
@@ -570,8 +569,6 @@ module Rack
     end
     module_function :status_code
 
-    Multipart = Rack::Multipart
-
     PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
 
     def clean_path_info(path_info)
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index 1f4c69fd..cfe508c1 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -306,7 +306,7 @@ describe Rack::Multipart do
 
   it "parse multipart/mixed" do
     env = Rack::MockRequest.env_for("/", multipart_fixture(:mixed_files))
-    params = Rack::Utils::Multipart.parse_multipart(env)
+    params = Rack::Multipart.parse_multipart(env)
     params["foo"].must_equal "bar"
     params["files"].must_be_instance_of String
     params["files"].size.must_equal 252
@@ -572,7 +572,7 @@ EOF
       :input => StringIO.new(data)
     }
     env = Rack::MockRequest.env_for("/", options)
-    params = Rack::Utils::Multipart.parse_multipart(env)
+    params = Rack::Multipart.parse_multipart(env)
 
     params.must_equal "description"=>"Very very blue"
   end
@@ -601,7 +601,7 @@ contents\r
       :input => StringIO.new(data)
     }
     env = Rack::MockRequest.env_for("/", options)
-    params = Rack::Utils::Multipart.parse_multipart(env)
+    params = Rack::Multipart.parse_multipart(env)
 
     params["file"][:filename].must_equal 'long' * 100
   end
@@ -655,7 +655,7 @@ Content-Type: image/png\r
       :input => StringIO.new(data)
     }
     env = Rack::MockRequest.env_for("/", options)
-    params = Rack::Utils::Multipart.parse_multipart(env)
+    params = Rack::Multipart.parse_multipart(env)
 
     params["text/plain"].must_equal ["some text", "some more text (I didn't specify Content-Type)"]
     params["image/png"].length.must_equal 1
diff --git a/test/spec_request.rb b/test/spec_request.rb
index 8b56b7f2..44fcfd45 100644
--- a/test/spec_request.rb
+++ b/test/spec_request.rb
@@ -6,19 +6,19 @@ require 'rack/mock'
 require 'rack/multipart'
 require 'securerandom'
 
-describe Rack::Request do
+class RackRequestTest < Minitest::Spec
   it "copies the env when duping" do
-    req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
+    req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
     refute_same req.env, req.dup.env
   end
 
   it "can get a key from the env" do
-    req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
+    req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
     assert_equal "example.com", req.get_header("SERVER_NAME")
   end
 
   it 'yields to the block if no value has been set' do
-    req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
+    req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
     yielded = false
     req.fetch_header("FOO") do
       yielded = true
@@ -30,18 +30,18 @@ describe Rack::Request do
   end
 
   it 'can set values in the env' do
-    req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
+    req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
     req.set_header("FOO", "BAR")
     assert_equal "BAR", req.get_header("FOO")
   end
 
   it 'can check if something has been set' do
-    req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
+    req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
     refute req.has_header?("FOO")
   end
 
   it 'can iterate over values' do
-    req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
+    req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
     req.set_header 'foo', 'bar'
     hash = {}
     req.each_header do |k,v|
@@ -51,7 +51,7 @@ describe Rack::Request do
   end
 
   it 'can delete env values' do
-    req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
+    req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
     req.set_header 'foo', 'bar'
     assert req.has_header? 'foo'
     req.delete_header 'foo'
@@ -59,7 +59,7 @@ describe Rack::Request do
   end
 
   it "wrap the rack variables" do
-    req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
+    req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
 
     req.body.must_respond_to :gets
     req.scheme.must_equal "http"
@@ -84,103 +84,103 @@ describe Rack::Request do
   end
 
   it "figure out the correct host" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
     req.host.must_equal "www2.example.org"
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
     req.host.must_equal "example.org"
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
     req.host.must_equal "example.org"
 
     env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292")
     env.delete("SERVER_NAME")
-    req = Rack::Request.new(env)
+    req = make_request(env)
     req.host.must_equal "192.168.1.1"
 
     env = Rack::MockRequest.env_for("/")
     env.delete("SERVER_NAME")
-    req = Rack::Request.new(env)
+    req = make_request(env)
     req.host.must_equal ""
   end
 
   it "figure out the correct port" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
     req.port.must_equal 80
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org:81")
     req.port.must_equal 81
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
     req.port.must_equal 9292
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
     req.port.must_equal 9292
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org")
     req.port.must_equal 80
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_SSL" => "on")
     req.port.must_equal 443
 
-     req = Rack::Request.new \
+     req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PROTO" => "https")
     req.port.must_equal 443
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PORT" => "9393")
     req.port.must_equal 9393
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9393", "SERVER_PORT" => "80")
     req.port.must_equal 9393
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
     req.port.must_equal 80
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https", "SERVER_PORT" => "80")
     req.port.must_equal 443
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https,https", "SERVER_PORT" => "80")
     req.port.must_equal 443
   end
 
   it "figure out the correct host with port" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
     req.host_with_port.must_equal "www2.example.org"
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81")
     req.host_with_port.must_equal "localhost:81"
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
     req.host_with_port.must_equal "example.org:9292"
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
     req.host_with_port.must_equal "example.org:9292"
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
     req.host_with_port.must_equal "example.org"
   end
 
   it "parse the query string" do
-    req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
+    req = make_request(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
     req.query_string.must_equal "foo=bar&quux=bla"
     req.GET.must_equal "foo" => "bar", "quux" => "bla"
     req.POST.must_be :empty?
@@ -191,7 +191,7 @@ describe Rack::Request do
     mr = Rack::MockRequest.env_for("/",
       "REQUEST_METHOD" => 'POST',
       :input => "foo=bar&quux=b;la")
-    req = Rack::Request.new mr
+    req = make_request mr
     req.query_string.must_equal ""
     req.GET.must_be :empty?
     req.POST.must_equal "foo" => "bar", "quux" => "b;la"
@@ -219,7 +219,7 @@ describe Rack::Request do
   end
 
   it "use semi-colons as separators for query strings in GET" do
-    req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=b;la;wun=duh"))
+    req = make_request(Rack::MockRequest.env_for("/?foo=bar&quux=b;la;wun=duh"))
     req.query_string.must_equal "foo=bar&quux=b;la;wun=duh"
     req.GET.must_equal "foo" => "bar", "quux" => "b", "la" => nil, "wun" => "duh"
     req.POST.must_be :empty?
@@ -231,7 +231,7 @@ describe Rack::Request do
 
     old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
     begin
-      req = Rack::Request.new(env)
+      req = make_request(env)
       lambda { req.GET }.must_raise RangeError
     ensure
       Rack::Utils.key_space_limit = old
@@ -245,8 +245,8 @@ describe Rack::Request do
     old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3
     begin
       exp = {"foo"=>{"bar"=>{"baz"=>{"qux"=>"1"}}}}
-      Rack::Request.new(nested_query).GET.must_equal exp
-      lambda { Rack::Request.new(plain_query).GET  }.must_raise RangeError
+      make_request(nested_query).GET.must_equal exp
+      lambda { make_request(plain_query).GET  }.must_raise RangeError
     ensure
       Rack::Utils.key_space_limit = old
     end
@@ -257,7 +257,7 @@ describe Rack::Request do
       "REQUEST_METHOD" => 'POST',
       :input => "foo=bar&quux=bla"
     )
-    req = Rack::Request.new mr
+    req = make_request mr
 
     req.params
 
@@ -299,19 +299,19 @@ describe Rack::Request do
       "REQUEST_METHOD" => 'POST',
       :input => "a%=1"
     )
-    req = Rack::Request.new mr
+    req = make_request mr
 
     lambda { req.POST }.must_raise(Rack::Utils::InvalidParameterError).
       message.must_equal "invalid %-encoding (a%)"
   end
 
   it "raise if rack.input is missing" do
-    req = Rack::Request.new({})
+    req = make_request({})
     lambda { req.POST }.must_raise RuntimeError
   end
 
   it "parse POST data when method is POST and no Content-Type given" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/?foo=quux",
         "REQUEST_METHOD" => 'POST',
         :input => "foo=bar&quux=bla")
@@ -330,7 +330,7 @@ describe Rack::Request do
 
     old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
     begin
-      req = Rack::Request.new(env)
+      req = make_request(env)
       lambda { req.POST }.must_raise RangeError
     ensure
       Rack::Utils.key_space_limit = old
@@ -338,7 +338,7 @@ describe Rack::Request do
   end
 
   it "parse POST data with explicit content type regardless of method" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/",
         "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
         :input => "foo=bar&quux=bla")
@@ -350,7 +350,7 @@ describe Rack::Request do
   end
 
   it "not parse POST data when media type is not form-data" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/?foo=quux",
         "REQUEST_METHOD" => 'POST',
         "CONTENT_TYPE" => 'text/plain;charset=utf-8',
@@ -364,7 +364,7 @@ describe Rack::Request do
   end
 
   it "parse POST data on PUT when media type is form-data" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/?foo=quux",
         "REQUEST_METHOD" => 'PUT',
         "CONTENT_TYPE" => 'application/x-www-form-urlencoded',
@@ -375,7 +375,7 @@ describe Rack::Request do
 
   it "rewind input after parsing POST data" do
     input = StringIO.new("foo=bar&quux=bla")
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/",
         "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
         :input => input)
@@ -390,7 +390,7 @@ describe Rack::Request do
       "CONTENT_LENGTH" => '0',
       :input => nil)
 
-    req = Rack::Request.new mr
+    req = make_request mr
     req.query_string.must_equal ""
     req.GET.must_be :empty?
     req.POST.must_be :empty?
@@ -398,26 +398,30 @@ describe Rack::Request do
   end
 
   it "clean up Safari's ajax POST body" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/",
         'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0")
     req.POST.must_equal "foo" => "bar", "quux" => "bla"
   end
 
   it "get value by key from params with #[]" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("?foo=quux")
     req['foo'].must_equal 'quux'
     req[:foo].must_equal 'quux'
   end
 
   it "set value to key on params with #[]=" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("?foo=duh")
     req['foo'].must_equal 'duh'
     req[:foo].must_equal 'duh'
     req.params.must_equal 'foo' => 'duh'
 
+    if req.delegate?
+      skip "delegate requests don't cache params, so mutations have no impact"
+    end
+
     req['foo'] = 'bar'
     req.params.must_equal 'foo' => 'bar'
     req['foo'].must_equal 'bar'
@@ -430,7 +434,7 @@ describe Rack::Request do
   end
 
   it "return values for the keys in the order given from values_at" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful")
     req.values_at('foo').must_equal ['baz']
     req.values_at('foo', 'wun').must_equal ['baz', 'der']
@@ -438,119 +442,119 @@ describe Rack::Request do
   end
 
   it "extract referrer correctly" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path")
     req.referer.must_equal "/some/path"
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/")
     req.referer.must_equal nil
   end
 
   it "extract user agent correctly" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)")
     req.user_agent.must_equal "Mozilla/4.0 (compatible)"
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/")
     req.user_agent.must_equal nil
   end
 
   it "treat missing content type as nil" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/")
     req.content_type.must_equal nil
   end
 
   it "treat empty content type as nil" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
     req.content_type.must_equal nil
   end
 
   it "return nil media type for empty content type" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
     req.media_type.must_equal nil
   end
 
   it "cache, but invalidates the cache" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/?foo=quux",
         "CONTENT_TYPE" => "application/x-www-form-urlencoded",
         :input => "foo=bar&quux=bla")
     req.GET.must_equal "foo" => "quux"
     req.GET.must_equal "foo" => "quux"
-    req.env["QUERY_STRING"] = "bla=foo"
+    req.set_header("QUERY_STRING", "bla=foo")
     req.GET.must_equal "bla" => "foo"
     req.GET.must_equal "bla" => "foo"
 
     req.POST.must_equal "foo" => "bar", "quux" => "bla"
     req.POST.must_equal "foo" => "bar", "quux" => "bla"
-    req.env["rack.input"] = StringIO.new("foo=bla&quux=bar")
+    req.set_header("rack.input", StringIO.new("foo=bla&quux=bar"))
     req.POST.must_equal "foo" => "bla", "quux" => "bar"
     req.POST.must_equal "foo" => "bla", "quux" => "bar"
   end
 
   it "figure out if called via XHR" do
-    req = Rack::Request.new(Rack::MockRequest.env_for(""))
+    req = make_request(Rack::MockRequest.env_for(""))
     req.wont_be :xhr?
 
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
     req.must_be :xhr?
   end
 
   it "ssl detection" do
-    request = Rack::Request.new(Rack::MockRequest.env_for("/"))
+    request = make_request(Rack::MockRequest.env_for("/"))
     request.scheme.must_equal "http"
     request.wont_be :ssl?
 
-    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTPS' => 'on'))
+    request = make_request(Rack::MockRequest.env_for("/", 'HTTPS' => 'on'))
     request.scheme.must_equal "https"
     request.must_be :ssl?
 
-    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https'))
+    request = make_request(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https'))
     request.scheme.must_equal "https"
     request.must_be :ssl?
 
-    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080'))
+    request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080'))
     request.scheme.must_equal "http"
     request.wont_be :ssl?
 
-    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on'))
+    request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on'))
     request.scheme.must_equal "https"
     request.must_be :ssl?
 
-    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on'))
+    request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on'))
     request.scheme.must_equal "https"
     request.must_be :ssl?
 
-    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'https'))
+    request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'https'))
     request.scheme.must_equal "https"
     request.must_be :ssl?
 
-    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https'))
+    request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https'))
     request.scheme.must_equal "https"
     request.must_be :ssl?
 
-    request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http'))
+    request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http'))
     request.scheme.must_equal "https"
     request.must_be :ssl?
   end
 
   it "parse cookies" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
     req.cookies.must_equal "foo" => "bar", "quux" => "h&m"
     req.cookies.must_equal "foo" => "bar", "quux" => "h&m"
-    req.env.delete("HTTP_COOKIE")
+    req.delete_header("HTTP_COOKIE")
     req.cookies.must_equal({})
   end
 
   it "always return the same hash object" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
     hash = req.cookies
     req.env.delete("HTTP_COOKIE")
@@ -560,7 +564,7 @@ describe Rack::Request do
   end
 
   it "modify the cookies hash in place" do
-    req = Rack::Request.new(Rack::MockRequest.env_for(""))
+    req = make_request(Rack::MockRequest.env_for(""))
     req.cookies.must_equal({})
     req.cookies['foo'] = 'bar'
     req.cookies.must_equal 'foo' => 'bar'
@@ -568,47 +572,50 @@ describe Rack::Request do
 
   it "not modify the params hash in place" do
     e = Rack::MockRequest.env_for("")
-    req1 = Rack::Request.new(e)
+    req1 = make_request(e)
+    if req1.delegate?
+      skip "delegate requests don't cache params, so mutations have no impact"
+    end
     req1.params.must_equal({})
     req1.params['foo'] = 'bar'
     req1.params.must_equal 'foo' => 'bar'
-    req2 = Rack::Request.new(e)
+    req2 = make_request(e)
     req2.params.must_equal({})
   end
 
   it "modify params hash if param is in GET" do
     e = Rack::MockRequest.env_for("?foo=duh")
-    req1 = Rack::Request.new(e)
+    req1 = make_request(e)
     req1.params.must_equal 'foo' => 'duh'
     req1.update_param 'foo', 'bar'
     req1.params.must_equal 'foo' => 'bar'
-    req2 = Rack::Request.new(e)
+    req2 = make_request(e)
     req2.params.must_equal 'foo' => 'bar'
   end
 
   it "modify params hash if param is in POST" do
     e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=duh')
-    req1 = Rack::Request.new(e)
+    req1 = make_request(e)
     req1.params.must_equal 'foo' => 'duh'
     req1.update_param 'foo', 'bar'
     req1.params.must_equal 'foo' => 'bar'
-    req2 = Rack::Request.new(e)
+    req2 = make_request(e)
     req2.params.must_equal 'foo' => 'bar'
   end
 
   it "modify params hash, even if param didn't exist before" do
     e = Rack::MockRequest.env_for("")
-    req1 = Rack::Request.new(e)
+    req1 = make_request(e)
     req1.params.must_equal({})
     req1.update_param 'foo', 'bar'
     req1.params.must_equal 'foo' => 'bar'
-    req2 = Rack::Request.new(e)
+    req2 = make_request(e)
     req2.params.must_equal 'foo' => 'bar'
   end
 
   it "modify params hash by changing only GET" do
     e = Rack::MockRequest.env_for("?foo=duhget")
-    req = Rack::Request.new(e)
+    req = make_request(e)
     req.GET.must_equal 'foo' => 'duhget'
     req.POST.must_equal({})
     req.update_param 'foo', 'bar'
@@ -618,7 +625,7 @@ describe Rack::Request do
 
   it "modify params hash by changing only POST" do
     e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost")
-    req = Rack::Request.new(e)
+    req = make_request(e)
     req.GET.must_equal({})
     req.POST.must_equal 'foo' => 'duhpost'
     req.update_param 'foo', 'bar'
@@ -628,7 +635,7 @@ describe Rack::Request do
 
   it "modify params hash, even if param is defined in both POST and GET" do
     e = Rack::MockRequest.env_for("?foo=duhget", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost")
-    req1 = Rack::Request.new(e)
+    req1 = make_request(e)
     req1.GET.must_equal 'foo' => 'duhget'
     req1.POST.must_equal 'foo' => 'duhpost'
     req1.params.must_equal 'foo' => 'duhpost'
@@ -636,7 +643,7 @@ describe Rack::Request do
     req1.GET.must_equal 'foo' => 'bar'
     req1.POST.must_equal 'foo' => 'bar'
     req1.params.must_equal 'foo' => 'bar'
-    req2 = Rack::Request.new(e)
+    req2 = make_request(e)
     req2.GET.must_equal 'foo' => 'bar'
     req2.POST.must_equal 'foo' => 'bar'
     req2.params.must_equal 'foo' => 'bar'
@@ -645,37 +652,37 @@ describe Rack::Request do
 
   it "allow deleting from params hash if param is in GET" do
     e = Rack::MockRequest.env_for("?foo=bar")
-    req1 = Rack::Request.new(e)
+    req1 = make_request(e)
     req1.params.must_equal 'foo' => 'bar'
     req1.delete_param('foo').must_equal 'bar'
     req1.params.must_equal({})
-    req2 = Rack::Request.new(e)
+    req2 = make_request(e)
     req2.params.must_equal({})
   end
 
   it "allow deleting from params hash if param is in POST" do
     e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=bar')
-    req1 = Rack::Request.new(e)
+    req1 = make_request(e)
     req1.params.must_equal 'foo' => 'bar'
     req1.delete_param('foo').must_equal 'bar'
     req1.params.must_equal({})
-    req2 = Rack::Request.new(e)
+    req2 = make_request(e)
     req2.params.must_equal({})
   end
 
   it "pass through non-uri escaped cookies as-is" do
-    req = Rack::Request.new Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%")
+    req = make_request Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%")
     req.cookies["foo"].must_equal "%"
   end
 
   it "parse cookies according to RFC 2109" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
     req.cookies.must_equal 'foo' => 'bar'
   end
 
   it 'parse cookies with quotes' do
-    req = Rack::Request.new Rack::MockRequest.env_for('', {
+    req = make_request Rack::MockRequest.env_for('', {
       'HTTP_COOKIE' => '$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"'
     })
     req.cookies.must_equal({
@@ -687,7 +694,7 @@ describe Rack::Request do
   end
 
   it "provide setters" do
-    req = Rack::Request.new(e=Rack::MockRequest.env_for(""))
+    req = make_request(e=Rack::MockRequest.env_for(""))
     req.script_name.must_equal ""
     req.script_name = "/foo"
     req.script_name.must_equal "/foo"
@@ -700,58 +707,58 @@ describe Rack::Request do
   end
 
   it "provide the original env" do
-    req = Rack::Request.new(e = Rack::MockRequest.env_for(""))
+    req = make_request(e = Rack::MockRequest.env_for(""))
     req.env.must_equal e
   end
 
   it "restore the base URL" do
-    Rack::Request.new(Rack::MockRequest.env_for("")).base_url.
+    make_request(Rack::MockRequest.env_for("")).base_url.
       must_equal "http://example.org"
-    Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url.
+    make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url.
       must_equal "http://example.org"
   end
 
   it "restore the URL" do
-    Rack::Request.new(Rack::MockRequest.env_for("")).url.
+    make_request(Rack::MockRequest.env_for("")).url.
       must_equal "http://example.org/"
-    Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
+    make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
       must_equal "http://example.org/foo/"
-    Rack::Request.new(Rack::MockRequest.env_for("/foo")).url.
+    make_request(Rack::MockRequest.env_for("/foo")).url.
       must_equal "http://example.org/foo"
-    Rack::Request.new(Rack::MockRequest.env_for("?foo")).url.
+    make_request(Rack::MockRequest.env_for("?foo")).url.
       must_equal "http://example.org/?foo"
-    Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).url.
+    make_request(Rack::MockRequest.env_for("http://example.org:8080/")).url.
       must_equal "http://example.org:8080/"
-    Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).url.
+    make_request(Rack::MockRequest.env_for("https://example.org/")).url.
       must_equal "https://example.org/"
-    Rack::Request.new(Rack::MockRequest.env_for("coffee://example.org/")).url.
+    make_request(Rack::MockRequest.env_for("coffee://example.org/")).url.
       must_equal "coffee://example.org/"
-    Rack::Request.new(Rack::MockRequest.env_for("coffee://example.org:443/")).url.
+    make_request(Rack::MockRequest.env_for("coffee://example.org:443/")).url.
       must_equal "coffee://example.org:443/"
-    Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url.
+    make_request(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url.
       must_equal "https://example.com:8080/foo?foo"
   end
 
   it "restore the full path" do
-    Rack::Request.new(Rack::MockRequest.env_for("")).fullpath.
+    make_request(Rack::MockRequest.env_for("")).fullpath.
       must_equal "/"
-    Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
+    make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
       must_equal "/foo/"
-    Rack::Request.new(Rack::MockRequest.env_for("/foo")).fullpath.
+    make_request(Rack::MockRequest.env_for("/foo")).fullpath.
       must_equal "/foo"
-    Rack::Request.new(Rack::MockRequest.env_for("?foo")).fullpath.
+    make_request(Rack::MockRequest.env_for("?foo")).fullpath.
       must_equal "/?foo"
-    Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath.
+    make_request(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath.
       must_equal "/"
-    Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).fullpath.
+    make_request(Rack::MockRequest.env_for("https://example.org/")).fullpath.
       must_equal "/"
 
-    Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath.
+    make_request(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath.
      must_equal "/foo?foo"
   end
 
   it "handle multiple media type parameters" do
-    req = Rack::Request.new \
+    req = make_request \
       Rack::MockRequest.env_for("/",
         "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam;blong="boo";zump="zoo\"o";weird=lol"')
       req.wont_be :form_data?
@@ -784,7 +791,7 @@ Content-Transfer-Encoding: base64\r
 /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
 --AaB03x--\r
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -823,7 +830,7 @@ Content-Transfer-Encoding: base64
 /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg
 --AaB03x--
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -847,7 +854,7 @@ Content-Transfer-Encoding: base64\r
 /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
 --AaB03x--\r
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -882,7 +889,7 @@ EOF
         :input => StringIO.new(data)
       }
 
-      request = Rack::Request.new Rack::MockRequest.env_for("/", options)
+      request = make_request Rack::MockRequest.env_for("/", options)
       lambda { request.POST }.must_raise Rack::Multipart::MultipartPartLimitError
     end
   end
@@ -904,7 +911,7 @@ EOF
         :input => StringIO.new(data)
       }
 
-      request = Rack::Request.new Rack::MockRequest.env_for("/", options)
+      request = make_request Rack::MockRequest.env_for("/", options)
       assert_raises(Rack::Multipart::MultipartPartLimitError) do
         request.POST
       end
@@ -925,7 +932,7 @@ content-disposition: form-data; name="mean"; filename="mean"\r
 --AaB03xha\r
 --AaB03x--\r
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -955,7 +962,7 @@ EOF
                           "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                           "CONTENT_LENGTH" => input.size,
                           :input => input)
-    req = Rack::Request.new(env)
+    req = make_request(env)
     req.params
     env['rack.tempfiles'].size.must_equal 2
   end
@@ -965,7 +972,7 @@ EOF
 --AaB03x\r
 content-disposition: form-data; name="huge"; filename="huge"\r
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -978,7 +985,7 @@ content-disposition: form-data; name="huge"; filename="huge"\r
 \r
 foo\r
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -991,7 +998,7 @@ content-disposition: form-data; name="huge"; filename="huge"\r
 \r
 foo\r
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -1004,7 +1011,7 @@ EOF
 --AaB03x\r
 content-disposition: form-data; name="huge"; filename="huge"\r
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -1023,7 +1030,7 @@ Content-Transfer-Encoding: 7bit\r
 foo\r
 --AaB03x--\r
 EOF
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "CONTENT_TYPE" => "multipart/related, boundary=AaB03x",
                       "CONTENT_LENGTH" => input.size,
                       :input => input)
@@ -1041,7 +1048,7 @@ content-type: application/octet-stream\r
 --AaB03x--\r
 EOF
 
-        req = Rack::Request.new Rack::MockRequest.env_for("/",
+        req = make_request Rack::MockRequest.env_for("/",
                           "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
                           "CONTENT_LENGTH" => input.size,
                           :input => input)
@@ -1056,7 +1063,7 @@ EOF
     rack_input.write(input)
     rack_input.rewind
 
-    req = Rack::Request.new Rack::MockRequest.env_for("/",
+    req = make_request Rack::MockRequest.env_for("/",
                       "rack.request.form_hash" => {'foo' => 'bar'},
                       "rack.request.form_input" => rack_input,
                       :input => rack_input)
@@ -1066,7 +1073,7 @@ EOF
 
   it "conform to the Rack spec" do
     app = lambda { |env|
-      content = Rack::Request.new(env).POST["file"].inspect
+      content = make_request(env).POST["file"].inspect
       size = content.bytesize
       [200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]]
     }
@@ -1094,7 +1101,7 @@ EOF
 
   it "parse Accept-Encoding correctly" do
     parser = lambda do |x|
-      Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
+      make_request(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
     end
 
     parser.call(nil).must_equal []
@@ -1111,7 +1118,7 @@ EOF
 
   it "parse Accept-Language correctly" do
     parser = lambda do |x|
-      Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_LANGUAGE" => x)).accept_language
+      make_request(Rack::MockRequest.env_for("", "HTTP_ACCEPT_LANGUAGE" => x)).accept_language
     end
 
     parser.call(nil).must_equal []
@@ -1126,12 +1133,14 @@ EOF
     parser.call("fr").must_equal [["fr", 1.0]]
   end
 
-  ip_app = lambda { |env|
-    request = Rack::Request.new(env)
-    response = Rack::Response.new
-    response.write request.ip
-    response.finish
-  }
+  def ip_app
+    lambda { |env|
+      request = make_request(env)
+      response = Rack::Response.new
+      response.write request.ip
+      response.finish
+    }
+  end
 
   it 'provide ip information' do
     mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
@@ -1245,7 +1254,7 @@ EOF
   end
 
   it "regard local addresses as proxies" do
-    req = Rack::Request.new(Rack::MockRequest.env_for("/"))
+    req = make_request(Rack::MockRequest.env_for("/"))
     req.trusted_proxy?('127.0.0.1').must_equal 0
     req.trusted_proxy?('10.0.0.1').must_equal 0
     req.trusted_proxy?('172.16.0.1').must_equal 0
@@ -1277,7 +1286,7 @@ EOF
   it "allow subclass request to be instantiated after parent request" do
     env = Rack::MockRequest.env_for("/?foo=bar")
 
-    req1 = Rack::Request.new(env)
+    req1 = make_request(env)
     req1.GET.must_equal "foo" => "bar"
     req1.params.must_equal "foo" => "bar"
 
@@ -1293,14 +1302,14 @@ EOF
     req1.GET.must_equal "foo" => "bar"
     req1.params.must_equal :foo => "bar"
 
-    req2 = Rack::Request.new(env)
+    req2 = make_request(env)
     req2.GET.must_equal "foo" => "bar"
     req2.params.must_equal "foo" => "bar"
   end
 
   it "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)
+    req = make_request(broken_query)
     lambda{req.GET}.must_raise TypeError
     lambda{req.params}.must_raise TypeError
   end
@@ -1311,9 +1320,41 @@ EOF
     it "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do
       url = "/?foo=#{c}bar#{c}"
       env = Rack::MockRequest.env_for(url)
-      req2 = Rack::Request.new(env)
+      req2 = make_request(env)
       req2.GET.must_equal "foo" => "#{b}bar#{b}"
       req2.params.must_equal "foo" => "#{b}bar#{b}"
     end
   }
+
+  class NonDelegate < Rack::Request
+    def delegate?; false; end
+  end
+
+  def make_request(env)
+    NonDelegate.new env
+  end
+
+  class TestProxyRequest < RackRequestTest
+    class DelegateRequest
+      include Rack::Request::Helpers
+      extend Forwardable
+
+      def_delegators :@req, :get_header, :fetch_header, :delete_header,
+        :set_header, :has_header?, :each_header
+
+      def_delegators :@req, :[], :[]=, :values_at
+
+      def initialize(req)
+        @req = req
+      end
+
+      def delegate?; true; end
+
+      def env; @req.env.dup; end
+    end
+
+    def make_request(env)
+      DelegateRequest.new super(env)
+    end
+  end
 end