Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 4388

Show
Ignore:
Timestamp:
06/01/06 00:01:48 (2 years ago)
Author:
bitsweat
Message:

Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. Accept multipart PUT parameters. Closes #5235.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/CHANGELOG

    r4384 r4388  
    11*SVN* 
     2 
     3* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. [Jeremy Kemper] 
     4 
     5* Accept multipart PUT parameters. #5235 [guy.naor@famundo.com] 
    26 
    37* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [DHH]. Example: 
  • trunk/actionpack/lib/action_controller/cgi_ext/raw_post_data_fix.rb

    r3137 r4388  
    11class CGI #:nodoc: 
    2   # Add @request.env['RAW_POST_DATA'] for the vegans. 
    32  module QueryExtension 
    43    # Initialize the data from the query. 
     
    65    # Handles multipart forms (in particular, forms that involve file uploads). 
    76    # Reads query parameters in the @params field, and cookies into @cookies. 
    8     def initialize_query() 
     7    def initialize_query 
    98      @cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE']) 
    109 
    11       #fix some strange request environments 
     10      # Fix some strange request environments. 
    1211      if method = env_table['REQUEST_METHOD'] 
    1312        method = method.to_s.downcase.intern 
     
    1615      end 
    1716 
    18       if method == :post && (boundary = multipart_form_boundary) 
    19         @multipart = true 
    20         @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) 
    21       else 
    22         @multipart = false 
    23         @params = CGI::parse(read_query_params(method) || "") 
     17      # POST assumes missing Content-Type is application/x-www-form-urlencoded. 
     18      content_type = env_table['CONTENT_TYPE'] 
     19      if content_type.blank? && method == :post 
     20        content_type = 'application/x-www-form-urlencoded' 
    2421      end 
     22 
     23      # Force content length to zero if missing. 
     24      content_length = env_table['CONTENT_LENGTH'].to_i 
     25 
     26      # Set multipart to false by default. 
     27      @multipart = false 
     28 
     29      # POST and PUT may have params in entity body. If content type is 
     30      # missing for POST, assume urlencoded. If content type is missing 
     31      # for PUT, don't assume anything and don't parse the parameters: 
     32      # it's likely binary data. 
     33      # 
     34      # The other HTTP methods have their params in the query string. 
     35      if method == :post || method == :put 
     36        if boundary = extract_multipart_form_boundary(content_type) 
     37          @multipart = true 
     38          @params = read_multipart(boundary, content_length) 
     39        elsif content_type.downcase != 'application/x-www-form-urlencoded' 
     40          read_params(method, content_length) 
     41          @params = {} 
     42        end 
     43      end 
     44 
     45      @params ||= CGI.parse(read_params(method, content_length)) 
    2546    end 
    2647 
     
    3051      end 
    3152 
    32       def multipart_form_boundary 
    33         MULTIPART_FORM_BOUNDARY_RE.match(env_table['CONTENT_TYPE']).to_a.pop 
     53      def extract_multipart_form_boundary(content_type) 
     54        MULTIPART_FORM_BOUNDARY_RE.match(content_type).to_a.pop 
    3455      end 
    3556 
    3657      if defined? MOD_RUBY 
    37         def read_params_from_query 
     58        def read_query 
    3859          Apache::request.args || '' 
    3960        end 
    4061      else 
    41         def read_params_from_query 
     62        def read_query 
    4263          # fixes CGI querystring parsing for lighttpd 
    4364          env_qs = env_table['QUERY_STRING'] 
     
    5071      end 
    5172 
    52       def read_params_from_post 
     73      def read_body(content_length) 
    5374        stdinput.binmode if stdinput.respond_to?(:binmode) 
    54         content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || '' 
    55         # fix for Safari Ajax postings that always append \000 
     75        content = stdinput.read(content_length) || '' 
     76        # Fix for Safari Ajax postings that always append \000 
    5677        content.chop! if content[-1] == 0 
    5778        content.gsub! /&_=$/, '' 
     
    5980      end 
    6081 
    61       def read_query_params(method
     82      def read_params(method, content_length
    6283        case method 
    6384          when :get 
    64             read_params_from_query 
     85            read_query 
    6586          when :post, :put 
    66             read_params_from_post 
     87            read_body(content_length) 
    6788          when :cmd 
    6889            read_from_cmdline 
    69           else # when :head, :delete, :options 
    70             read_params_from_query 
     90          else # :head, :delete, :options, :trace, :connect 
     91            read_query 
    7192        end 
    7293      end 
  • trunk/actionpack/test/controller/cgi_test.rb

    r4343 r4388  
    236236end 
    237237 
     238 
    238239class MultipartCGITest < Test::Unit::TestCase 
    239240  FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart' 
     
    316317end 
    317318 
     319# Ensures that PUT works with multipart as well as POST. 
     320class PutMultipartCGITest < MultipartCGITest 
     321  def setup 
     322    super 
     323    ENV['REQUEST_METHOD'] = 'PUT' 
     324  end 
     325end 
     326 
    318327 
    319328class CGIRequestTest < Test::Unit::TestCase 
  • trunk/actionpack/test/controller/raw_post_test.rb

    r468 r4388  
    66class RawPostDataTest < Test::Unit::TestCase 
    77  def setup 
     8    ENV.delete('RAW_POST_DATA') 
     9    @request_body = 'a=1' 
     10  end 
     11 
     12  def test_post_with_urlencoded_body 
     13    ENV['REQUEST_METHOD'] = 'POST' 
     14    ENV['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' 
     15    assert_equal ['1'], cgi_params['a'] 
     16    assert_has_raw_post_data 
     17  end 
     18 
     19  def test_post_with_empty_content_type_treated_as_urlencoded 
    820    ENV['REQUEST_METHOD'] = 'POST' 
    921    ENV['CONTENT_TYPE'] = '' 
    10     ENV['CONTENT_LENGTH'] = '0' 
     22    assert_equal ['1'], cgi_params['a'] 
     23    assert_has_raw_post_data 
    1124  end 
    1225 
    13   def test_raw_post_data 
    14     process_raw "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1" 
     26  def test_post_with_unrecognized_content_type_reads_body_but_doesnt_parse_params 
     27    ENV['REQUEST_METHOD'] = 'POST' 
     28    ENV['CONTENT_TYPE'] = 'foo/bar' 
     29    assert cgi_params.empty? 
     30    assert_has_raw_post_data 
     31  end 
     32 
     33  def test_put_with_urlencoded_body 
     34    ENV['REQUEST_METHOD'] = 'PUT' 
     35    ENV['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' 
     36    assert_equal ['1'], cgi_params['a'] 
     37    assert_has_raw_post_data 
     38  end 
     39 
     40  def test_put_with_empty_content_type_ignores_body 
     41    ENV['REQUEST_METHOD'] = 'PUT' 
     42    ENV['CONTENT_TYPE'] = '' 
     43    assert cgi_params.empty? 
     44    assert_has_raw_post_data 
     45  end 
     46 
     47  def test_put_with_unrecognized_content_type_ignores_body 
     48    ENV['REQUEST_METHOD'] = 'PUT' 
     49    ENV['CONTENT_TYPE'] = 'foo/bar' 
     50    assert cgi_params.empty? 
     51    assert_has_raw_post_data 
    1552  end 
    1653 
    1754  private 
    18     def process_raw(query_string) 
    19       old_stdin = $stdin 
    20       begin 
    21         $stdin = StringIO.new(query_string.dup) 
    22         ENV['CONTENT_LENGTH'] = $stdin.size.to_s 
    23         CGI.new 
    24         assert_not_nil ENV['RAW_POST_DATA'] 
    25         assert ENV['RAW_POST_DATA'].frozen? 
    26         assert_equal query_string, ENV['RAW_POST_DATA'] 
    27       ensure 
    28         $stdin = old_stdin 
    29       end 
     55    def cgi_params 
     56      old_stdin, $stdin = $stdin, StringIO.new(@request_body.dup) 
     57      ENV['CONTENT_LENGTH'] = $stdin.size.to_s 
     58      CGI.new.params 
     59    ensure 
     60      $stdin = old_stdin 
     61    end 
     62 
     63    def assert_has_raw_post_data(expected_body = @request_body) 
     64      assert_not_nil ENV['RAW_POST_DATA'] 
     65      assert ENV['RAW_POST_DATA'].frozen? 
     66      assert_equal expected_body, ENV['RAW_POST_DATA'] 
    3067    end 
    3168end