Changeset 6740
- Timestamp:
- 05/15/07 21:36:21 (1 year ago)
- Files:
-
- trunk/actionpack/CHANGELOG (modified) (1 diff)
- trunk/actionpack/lib/action_controller/cgi_ext.rb (modified) (2 diffs)
- trunk/actionpack/lib/action_controller/cgi_ext/parameters.rb (modified) (2 diffs)
- trunk/actionpack/lib/action_controller/cgi_ext/query_extension.rb (modified) (2 diffs)
- trunk/actionpack/lib/action_controller/cgi_ext/stdinput.rb (added)
- trunk/actionpack/lib/action_controller/cgi_process.rb (modified) (2 diffs)
- trunk/actionpack/lib/action_controller/request.rb (modified) (3 diffs)
- trunk/actionpack/lib/action_controller/test_process.rb (modified) (2 diffs)
- trunk/actionpack/test/abstract_unit.rb (modified) (1 diff)
- trunk/actionpack/test/controller/raw_post_test.rb (modified) (6 diffs)
- trunk/actionpack/test/controller/test_test.rb (modified) (2 diffs)
- trunk/actionpack/test/controller/webservice_test.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/CHANGELOG
r6736 r6740 1 1 *SVN* 2 3 * Introduce the request.body stream. Lazy-read to parse parameters rather than always setting RAW_POST_DATA. Reduces the memory footprint of large binary PUT requests. [Jeremy Kemper] 2 4 3 5 * Add some performance enhancements to ActionView. trunk/actionpack/lib/action_controller/cgi_ext.rb
r6733 r6740 1 require 'action_controller/cgi_ext/stdinput' 1 2 require 'action_controller/cgi_ext/parameters' 2 3 require 'action_controller/cgi_ext/query_extension' … … 5 6 6 7 class CGI #:nodoc: 8 include ActionController::CgiExt::Stdinput 7 9 include ActionController::CgiExt::Parameters 10 11 class << self 12 alias :escapeHTML_fail_on_nil :escapeHTML 13 14 def escapeHTML(string) 15 escapeHTML_fail_on_nil(string) unless string.nil? 16 end 17 end 8 18 end trunk/actionpack/lib/action_controller/cgi_ext/parameters.rb
r6733 r6740 1 1 require 'cgi' 2 2 require 'strscan' 3 4 class CGI #:nodoc:5 class << self6 alias :escapeHTML_fail_on_nil :escapeHTML7 8 def escapeHTML(string)9 escapeHTML_fail_on_nil(string) unless string.nil?10 end11 end12 end13 3 14 4 module ActionController … … 73 63 end 74 64 75 def parse_formatted_request_parameters(mime_type, raw_post_data)65 def parse_formatted_request_parameters(mime_type, body) 76 66 case strategy = ActionController::Base.param_parsers[mime_type] 77 67 when Proc 78 strategy.call( raw_post_data)68 strategy.call(body) 79 69 when :xml_simple, :xml_node 80 raw_post_data.blank? ? {} : Hash.from_xml(raw_post_data).with_indifferent_access70 body.blank? ? {} : Hash.from_xml(body).with_indifferent_access 81 71 when :yaml 82 YAML.load( raw_post_data)72 YAML.load(body) 83 73 end 84 74 rescue Exception => e # YAML, XML or Ruby code block errors 85 75 { "exception" => "#{e.message} (#{e.class})", "backtrace" => e.backtrace, 86 " raw_post_data" => raw_post_data, "format" => mime_type }76 "body" => body, "format" => mime_type } 87 77 end 88 78 trunk/actionpack/lib/action_controller/cgi_ext/query_extension.rb
r6733 r6740 32 32 @multipart = false 33 33 34 # POST and PUT may have params in entity body. If content type is 35 # missing for POST, assume urlencoded. If content type is missing 36 # for PUT, don't assume anything and don't parse the parameters: 37 # it's likely binary data. 38 # 39 # The other HTTP methods have their params in the query string. 34 # POST and PUT may have params in entity body. If content type is missing 35 # or non-urlencoded, don't read the body or parse parameters: assume it's 36 # binary data. 40 37 if method == :post || method == :put 41 38 if boundary = extract_multipart_form_boundary(content_type) … … 43 40 @params = read_multipart(boundary, content_length) 44 41 elsif content_type.blank? || content_type !~ %r{application/x-www-form-urlencoded}i 45 read_params(method, content_length)46 42 @params = {} 47 43 end trunk/actionpack/lib/action_controller/cgi_process.rb
r6733 r6740 59 59 end 60 60 61 # The request body is an IO input stream. If the RAW_POST_DATA environment 62 # variable is already set, wrap it in a StringIO. 63 def body 64 if raw_post = env['RAW_POST_DATA'] 65 StringIO.new(raw_post) 66 else 67 @cgi.stdinput 68 end 69 end 70 61 71 def query_parameters 62 72 @query_parameters ||= … … 67 77 @request_parameters ||= 68 78 if ActionController::Base.param_parsers.has_key?(content_type) 69 CGI.parse_formatted_request_parameters(content_type, @env['RAW_POST_DATA'])79 CGI.parse_formatted_request_parameters(content_type, body.read) 70 80 else 71 81 CGI.parse_request_parameters(@cgi.params) trunk/actionpack/lib/action_controller/request.rb
r6517 r6740 1 1 module ActionController 2 # Subclassing AbstractRequest makes these methods available to the request objects used in production and testing, 3 # CgiRequest and TestRequest 2 # CgiRequest and TestRequest provide concrete implementations. 4 3 class AbstractRequest 5 4 cattr_accessor :relative_url_root 6 5 remove_method :relative_url_root 7 6 8 # Returns the hash of environment variables for this request,7 # The hash of environment variables for this request, 9 8 # such as { 'RAILS_ENV' => 'production' }. 10 9 attr_reader :env 11 10 12 attr_accessor :format 13 14 # Returns the HTTP request method as a lowercase symbol (:get, for example). Note, HEAD is returned as :get 15 # since the two are supposedly to be functionaly equivilent for all purposes except that HEAD won't return a response 16 # body (which Rails also takes care of elsewhere). 11 # The requested content type, such as :html or :xml. 12 attr_writer :format 13 14 # The HTTP request method as a lowercase symbol, such as :get. 15 # Note, HEAD is returned as :get since the two are functionally 16 # equivalent from the application's perspective. 17 17 def method 18 @request_method ||= (!parameters[:_method].blank? && @env['REQUEST_METHOD'] == 'POST') ? 19 parameters[:_method].to_s.downcase.to_sym : 20 @env['REQUEST_METHOD'].downcase.to_sym 21 18 @request_method ||= 19 if @env['REQUEST_METHOD'] == 'POST' && !parameters[:_method].blank? 20 parameters[:_method].to_s.downcase.to_sym 21 else 22 @env['REQUEST_METHOD'].downcase.to_sym 23 end 24 22 25 @request_method == :head ? :get : @request_method 23 26 end … … 43 46 end 44 47 45 # Is this a HEAD request? HEAD is mapped as :get for request.method, so here we ask the46 # REQUEST_METHOD header directly. Thus, for head, both get? and head? will return true.48 # Is this a HEAD request? request.method sees HEAD as :get, so check the 49 # HTTP method directly. 47 50 def head? 48 51 @env['REQUEST_METHOD'].downcase.to_sym == :head … … 270 273 # Must be implemented in the concrete request 271 274 #++ 275 276 # The request body is an IO input stream. 277 def body 278 end 279 272 280 def query_parameters #:nodoc: 273 281 end trunk/actionpack/lib/action_controller/test_process.rb
r6350 r6740 41 41 end 42 42 43 # Wraps raw_post in a StringIO. 44 def body 45 StringIO.new(raw_post) 46 end 47 48 # Either the RAW_POST_DATA environment variable or the URL-encoded request 49 # parameters. 43 50 def raw_post 44 if raw_post = env['RAW_POST_DATA'] 45 raw_post 46 else 47 params = self.request_parameters.dup 48 %w(controller action only_path).each do |k| 49 params.delete(k) 50 params.delete(k.to_sym) 51 end 52 53 params.map { |k,v| [ CGI.escape(k.to_s), CGI.escape(v.to_s) ].join('=') }.sort.join('&') 54 end 51 env['RAW_POST_DATA'] ||= url_encoded_request_parameters 55 52 end 56 53 … … 140 137 @env["SERVER_PORT"] = 80 141 138 @env['REQUEST_METHOD'] = "GET" 139 end 140 141 def url_encoded_request_parameters 142 params = self.request_parameters.dup 143 144 %w(controller action only_path).each do |k| 145 params.delete(k) 146 params.delete(k.to_sym) 147 end 148 149 params.to_query 142 150 end 143 151 end trunk/actionpack/test/abstract_unit.rb
r6611 r6740 4 4 5 5 require 'yaml' 6 require 'stringio' 6 7 require 'test/unit' 7 8 require 'action_controller' 9 require 'action_controller/cgi_ext' 8 10 require 'action_controller/test_process' 9 11 trunk/actionpack/test/controller/raw_post_test.rb
r6733 r6740 1 1 require "#{File.dirname(__FILE__)}/../abstract_unit" 2 require 'stringio'3 require 'action_controller/cgi_ext/query_extension'4 2 5 3 class RawPostDataTest < Test::Unit::TestCase … … 12 10 ENV['REQUEST_METHOD'] = 'POST' 13 11 ENV['CONTENT_TYPE'] = ' apPlication/x-Www-form-urlEncoded; charset=utf-8' 14 assert_equal ['1'], cgi _params['a']15 assert_ has_raw_post_data12 assert_equal ['1'], cgi.params['a'] 13 assert_raw_post_data 16 14 end 17 15 … … 19 17 ENV['REQUEST_METHOD'] = 'POST' 20 18 ENV['CONTENT_TYPE'] = '' 21 assert_equal ['1'], cgi _params['a']22 assert_ has_raw_post_data19 assert_equal ['1'], cgi.params['a'] 20 assert_raw_post_data 23 21 end 24 22 25 def test_post_with_unrecognized_content_type_ reads_body_but_doesnt_parse_params23 def test_post_with_unrecognized_content_type_ignores_body 26 24 ENV['REQUEST_METHOD'] = 'POST' 27 25 ENV['CONTENT_TYPE'] = 'foo/bar' 28 assert cgi _params.empty?29 assert_ has_raw_post_data26 assert cgi.params.empty? 27 assert_no_raw_post_data 30 28 end 31 29 … … 33 31 ENV['REQUEST_METHOD'] = 'PUT' 34 32 ENV['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' 35 assert_equal ['1'], cgi _params['a']36 assert_ has_raw_post_data33 assert_equal ['1'], cgi.params['a'] 34 assert_raw_post_data 37 35 end 38 36 … … 40 38 ENV['REQUEST_METHOD'] = 'PUT' 41 39 ENV['CONTENT_TYPE'] = '' 42 assert cgi _params.empty?43 assert_ has_raw_post_data40 assert cgi.params.empty? 41 assert_no_raw_post_data 44 42 end 45 43 … … 47 45 ENV['REQUEST_METHOD'] = 'PUT' 48 46 ENV['CONTENT_TYPE'] = 'foo/bar' 49 assert cgi _params.empty?50 assert_ has_raw_post_data47 assert cgi.params.empty? 48 assert_no_raw_post_data 51 49 end 52 50 53 51 private 54 def cgi_params 55 old_stdin, $stdin = $stdin, StringIO.new(@request_body.dup) 56 ENV['CONTENT_LENGTH'] = $stdin.size.to_s 57 CGI.new.params 58 ensure 59 $stdin = old_stdin 52 def cgi 53 unless defined? @cgi 54 ENV['CONTENT_LENGTH'] = @request_body.size.to_s 55 @cgi = CGI.new('query', StringIO.new(@request_body.dup)) 56 end 57 58 @cgi 60 59 end 61 60 62 def assert_ has_raw_post_data(expected_body = @request_body)61 def assert_raw_post_data 63 62 assert_not_nil ENV['RAW_POST_DATA'] 64 63 assert ENV['RAW_POST_DATA'].frozen? 65 assert_equal expected_body, ENV['RAW_POST_DATA'] 64 assert_equal @request_body, ENV['RAW_POST_DATA'] 65 66 assert_equal '', cgi.stdinput.read 67 end 68 69 def assert_no_raw_post_data 70 assert_nil ENV['RAW_POST_DATA'] 71 72 assert_equal @request_body, cgi.stdinput.read 66 73 end 67 74 end trunk/actionpack/test/controller/test_test.rb
r6559 r6740 12 12 raise Test::Unit::AssertionFailedError, "#raw_post is blank" if request.raw_post.blank? 13 13 render :text => request.raw_post 14 end 15 16 def render_body 17 render :text => request.body.read 14 18 end 15 19 … … 94 98 get :render_raw_post, params.dup 95 99 96 raw_post = params.map {|k,v| [CGI::escape(k.to_s), CGI::escape(v.to_s)].join('=')}.sort.join('&') 97 assert_equal raw_post, @response.body 100 assert_equal params.to_query, @response.body 101 end 102 103 def test_body_stream 104 params = { :page => { :name => 'page name' }, 'some key' => 123 } 105 106 get :render_body, params.dup 107 108 assert_equal params.to_query, @response.body 98 109 end 99 110 trunk/actionpack/test/controller/webservice_test.rb
r6511 r6740 1 1 require File.dirname(__FILE__) + '/../abstract_unit' 2 require 'stringio'3 2 4 3 class WebServiceTest < Test::Unit::TestCase 4 class MockCGI < CGI #:nodoc: 5 attr_accessor :stdoutput, :env_table 5 6 6 class MockCGI < CGI #:nodoc: 7 attr_accessor :stdinput, :stdoutput, :env_table 8 9 def initialize(env, data = '') 7 def initialize(env, data = '') 10 8 self.env_table = env 11 self.stdinput = StringIO.new(data)12 9 self.stdoutput = StringIO.new 13 super( )10 super(nil, StringIO.new(data)) 14 11 end 15 12 end 16 17 13 18 14 class TestController < ActionController::Base