Permalink
Browse files

Build query string and POST params parser on top of Rack::Request. Al…

…so switch our multipart parser to use Racks. Moved XML, JSON, and YAML parsers into ActionController::ParamsParser middleware [#1661 state:resolved]
  • Loading branch information...
1 parent aab760c commit ff0a2678c4bce9da348e1263915558795e3a3640 @josh josh committed Jan 18, 2009
@@ -58,6 +58,7 @@ def self.load_all!
autoload :Layout, 'action_controller/layout'
autoload :MiddlewareStack, 'action_controller/middleware_stack'
autoload :MimeResponds, 'action_controller/mime_responds'
+ autoload :ParamsParser, 'action_controller/params_parser'
autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes'
autoload :RecordIdentifier, 'action_controller/record_identifier'
autoload :Request, 'action_controller/request'
@@ -74,6 +75,7 @@ def self.load_all!
autoload :TestCase, 'action_controller/test_case'
autoload :TestProcess, 'action_controller/test_process'
autoload :Translation, 'action_controller/translation'
+ autoload :UploadedFile, 'action_controller/uploaded_file'
autoload :UploadedStringIO, 'action_controller/uploaded_file'
autoload :UploadedTempfile, 'action_controller/uploaded_file'
autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser'
@@ -301,10 +301,7 @@ class Base
# A YAML parser is also available and can be turned on with:
#
# ActionController::Base.param_parsers[Mime::YAML] = :yaml
- @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
- Mime::URL_ENCODED_FORM => :url_encoded_form,
- Mime::XML => :xml_simple,
- Mime::JSON => :json }
+ @@param_parsers = {}
cattr_accessor :param_parsers
# Controls the default charset for all renders.
@@ -19,4 +19,5 @@
end
use ActionController::RewindableInput
+use ActionController::ParamsParser
use Rack::MethodOverride
@@ -0,0 +1,71 @@
+module ActionController
+ class ParamsParser
+ ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+ ActionController::Base.param_parsers[Mime::JSON] = :json
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ if params = parse_formatted_parameters(env)
+ env["action_controller.request.request_parameters"] = params
+ end
+
+ @app.call(env)
+ end
+
+ private
+ def parse_formatted_parameters(env)
+ request = Request.new(env)
+
+ return false if request.content_length.zero?
+
+ mime_type = content_type_from_legacy_post_data_format_header(env) || request.content_type
+ strategy = ActionController::Base.param_parsers[mime_type]
+
+ return false unless strategy
+
+ case strategy
+ when Proc
+ strategy.call(request.raw_post)
+ when :xml_simple, :xml_node
+ body = request.raw_post
+ body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
+ when :yaml
+ YAML.load(request.raw_post)
+ when :json
+ body = request.raw_post
+ if body.blank?
+ {}
+ else
+ data = ActiveSupport::JSON.decode(body)
+ data = {:_json => data} unless data.is_a?(Hash)
+ data.with_indifferent_access
+ end
+ else
+ false
+ end
+ rescue Exception => e # YAML, XML or Ruby code block errors
+ raise
+ { "body" => request.raw_post,
+ "content_type" => request.content_type,
+ "content_length" => request.content_length,
+ "exception" => "#{e.message} (#{e.class})",
+ "backtrace" => e.backtrace }
+ end
+
+ def content_type_from_legacy_post_data_format_header(env)
+ if x_post_format = env['HTTP_X_POST_DATA_FORMAT']
+ case x_post_format.to_s.downcase
+ when 'yaml'
+ return Mime::YAML
+ when 'xml'
+ return Mime::XML
+ end
+ end
+
+ nil
+ end
+ end
+end
@@ -1,2 +1,3 @@
require 'action_controller/rack_ext/lock'
require 'action_controller/rack_ext/multipart'
+require 'action_controller/rack_ext/parse_query'
@@ -0,0 +1,18 @@
+# Rack does not automatically cleanup Safari 2 AJAX POST body
+# This has not yet been commited to Rack, please +1 this ticket:
+# http://rack.lighthouseapp.com/projects/22435/tickets/19
+
+module Rack
+ module Utils
+ alias_method :parse_query_without_ajax_body_cleanup, :parse_query
+ module_function :parse_query_without_ajax_body_cleanup
+
+ def parse_query(qs, d = '&;')
+ qs = qs.dup
+ qs.chop! if qs[-1] == 0
+ qs.gsub!(/&_=$/, '')
+ parse_query_without_ajax_body_cleanup(qs, d)
+ end
+ module_function :parse_query
+ end
+end
@@ -9,11 +9,6 @@ module ActionController
class Request < Rack::Request
extend ActiveSupport::Memoizable
- def initialize(env)
- super
- @parser = ActionController::RequestParser.new(env)
- end
-
%w[ AUTH_TYPE GATEWAY_INTERFACE
PATH_TRANSLATED REMOTE_HOST
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
@@ -92,7 +87,11 @@ def content_length
# For backward compatibility, the post \format is extracted from the
# X-Post-Data-Format HTTP header if present.
def content_type
- Mime::Type.lookup(@parser.content_type_without_parameters)
+ if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
+ Mime::Type.lookup($1.strip.downcase)
+ else
+ nil
+ end
end
memoize :content_type
@@ -380,7 +379,11 @@ def path
# Read the request \body. This is useful for web services that need to
# work with raw requests directly.
def raw_post
- @parser.raw_post
+ unless @env.include? 'RAW_POST_DATA'
+ @env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i)
+ body.rewind if body.respond_to?(:rewind)
+ end
+ @env['RAW_POST_DATA']
end
# Returns both GET and POST \parameters in a single hash.
@@ -409,19 +412,30 @@ def path_parameters
@env["rack.routing_args"] ||= {}
end
+ # The request body is an IO input stream. If the RAW_POST_DATA environment
+ # variable is already set, wrap it in a StringIO.
def body
- @parser.body
+ if raw_post = @env['RAW_POST_DATA']
+ raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
+ StringIO.new(raw_post)
+ else
+ @env['rack.input']
+ end
+ end
+
+ def form_data?
+ FORM_DATA_MEDIA_TYPES.include?(content_type.to_s)
end
# Override Rack's GET method to support nested query strings
def GET
- @parser.query_parameters
+ @env["action_controller.request.query_parameters"] ||= UrlEncodedPairParser.parse_query_parameters(query_string)
end
alias_method :query_parameters, :GET
# Override Rack's POST method to support nested query strings
def POST
- @parser.request_parameters
+ @env["action_controller.request.request_parameters"] ||= UrlEncodedPairParser.parse_hash_parameters(super)
end
alias_method :request_parameters, :POST
Oops, something went wrong.

1 comment on commit ff0a267

Contributor

oggy commented on ff0a267 Jan 21, 2009

Josh,

You forgot to remove the autoload line for ‘action_controller/request_parser’.

Please sign in to comment.