From aea2d608fe592a665741087a8d6d31fcf26848ec Mon Sep 17 00:00:00 2001 From: Ryan Tomayko Date: Thu, 15 Jan 2009 05:46:21 -0800 Subject: Use Content-Type to determine POST params parsing [#20] Reverts the hard test for a 'PUT' request method (8d01dc0) and uses the Content-Type to determine whether to read into the request body. The Request#POST method parses the request body if (and only if) either of the following conditions are met: 1. The request's Content-Type is application/x-www-form-urlencoded or multipart/form-data. Note: the REQUEST_METHOD is ignored in this case. 2. The original REQUEST_METHOD is 'POST' and no Content-Type header was specified in the request. Note that we use the REQUEST_METHOD value before any modifications by the MethodOverride middleware. This is very similar to how this worked prior to 8d01dc0 but narrows the 'no Content-Type' special case to apply only to POST requests. A PUT request with no Content-Type header would trigger parsing before - with this change only POST requests with no Content-Type trigger parsing. --- lib/rack/request.rb | 14 +++++++++----- test/spec_rack_request.rb | 26 +++++++++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/rack/request.rb b/lib/rack/request.rb index 957abbb5..b3de1ce4 100644 --- a/lib/rack/request.rb +++ b/lib/rack/request.rb @@ -90,7 +90,6 @@ module Rack # one of the media types presents in this list will not be eligible # for form-data / param parsing. FORM_DATA_MEDIA_TYPES = [ - nil, 'application/x-www-form-urlencoded', 'multipart/form-data' ] @@ -104,12 +103,17 @@ module Rack ] # Determine whether the request body contains form-data by checking - # the request media_type against registered form-data media-types: - # "application/x-www-form-urlencoded" and "multipart/form-data". The + # the request Content-Type for one of the media-types: + # "application/x-www-form-urlencoded" or "multipart/form-data". The # list of form-data media types can be modified through the # +FORM_DATA_MEDIA_TYPES+ array. + # + # A request body is also assumed to contain form-data when no + # Content-Type header is provided and the request_method is POST. def form_data? - FORM_DATA_MEDIA_TYPES.include?(media_type) + type = media_type + meth = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'] + (meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type) end # Determine whether the request body contains data by checking @@ -158,7 +162,7 @@ module Rack # The union of GET and POST data. def params - self.put? ? self.GET : self.GET.update(self.POST) + self.GET.update(self.POST) rescue EOFError => e self.GET end diff --git a/test/spec_rack_request.rb b/test/spec_rack_request.rb index f0e1a5ca..fcdeb484 100644 --- a/test/spec_rack_request.rb +++ b/test/spec_rack_request.rb @@ -66,9 +66,11 @@ context "Rack::Request" do lambda { req.POST }.should.raise(RuntimeError) end - specify "can parse POST data" do + specify "can parse POST data when method is POST and no Content-Type given" do req = Rack::Request.new \ - Rack::MockRequest.env_for("/?foo=quux", :input => "foo=bar&quux=bla") + Rack::MockRequest.env_for("/?foo=quux", + "REQUEST_METHOD" => 'POST', + :input => "foo=bar&quux=bla") req.content_type.should.be.nil req.media_type.should.be.nil req.query_string.should.equal "foo=quux" @@ -77,7 +79,7 @@ context "Rack::Request" do req.params.should.equal "foo" => "bar", "quux" => "bla" end - specify "can parse POST data with explicit content type" do + specify "can parse POST data with explicit content type regardless of method" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar', @@ -92,6 +94,7 @@ context "Rack::Request" do specify "does not parse POST data when media type is not form-data" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", + "REQUEST_METHOD" => 'POST', "CONTENT_TYPE" => 'text/plain;charset=utf-8', :input => "foo=bar&quux=bla") req.content_type.should.equal 'text/plain;charset=utf-8' @@ -102,6 +105,16 @@ context "Rack::Request" do req.body.read.should.equal "foo=bar&quux=bla" end + specify "can parse POST data on PUT when media type is form-data" do + req = Rack::Request.new \ + Rack::MockRequest.env_for("/?foo=quux", + "REQUEST_METHOD" => 'PUT', + "CONTENT_TYPE" => 'application/x-www-form-urlencoded', + :input => "foo=bar&quux=bla") + req.POST.should.equal "foo" => "bar", "quux" => "bla" + req.body.read.should.equal "foo=bar&quux=bla" + end + specify "rewinds input after parsing POST data" do input = StringIO.new("foo=bar&quux=bla") req = Rack::Request.new \ @@ -114,7 +127,8 @@ context "Rack::Request" do specify "cleans up Safari's ajax POST body" do req = Rack::Request.new \ - Rack::MockRequest.env_for("/", :input => "foo=bar&quux=bla\0") + Rack::MockRequest.env_for("/", + 'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0") req.POST.should.equal "foo" => "bar", "quux" => "bla" end @@ -173,7 +187,9 @@ context "Rack::Request" do specify "can cache, but invalidates the cache" do req = Rack::Request.new \ - Rack::MockRequest.env_for("/?foo=quux", :input => "foo=bar&quux=bla") + Rack::MockRequest.env_for("/?foo=quux", + "CONTENT_TYPE" => "application/x-www-form-urlencoded", + :input => "foo=bar&quux=bla") req.GET.should.equal "foo" => "quux" req.GET.should.equal "foo" => "quux" req.env["QUERY_STRING"] = "bla=foo" -- cgit v1.2.3-24-ge0c7