BUG: HTTP body content get parsed in PUT call


#1

I’m developing a Rail application to accept data upload using HTTP PUT
call. Support for PUT in Rails seems to be broken in query params
parsing. I have a patch to fix the problem, but I’m not sure if it is
the “right” solution. Can some Rails developers take a quick look? I
can really use some help. :slight_smile:

Here is the evident of broken param parsing.

Processing FoobarController#put_action (for 127.0.0.1 at 2006-01-13

23:12:54)
Parameters: {“action”=>“put_action”, “key1”=>“value1”,
“controller”=>“foobar”, “Hello, I am a test upload via PUT.\n”=>""}

The content of the PUT-call “Hello, I am a test upload via PUT.\n” get
parsed as a param key. I made the PUT call using

curl -T test-file.txt

http://localhost:3000/foobar/put_action?key1=value1

From action_controller/cgi_ext/raw_post_data_fix.rb, the PUT method is
handled the same way as POST, which would have the content body parsed
for params. So, I made a patch by redefining
CGI::QueryExtension#read_query_params

Note that I have to put the body content into env_table[‘RAW_PUT_DATA’]
instead of env_table[‘RAW_POST_DATA’]. The latter will have the same
problem.

class CGI #:nodoc:
module QueryExtension
private
def setup_raw_put_data
stdinput.binmode if stdinput.respond_to?(:binmode)
content = stdinput.read(Integer(env_table[‘CONTENT_LENGTH’]))
|| ‘’
env_table[‘RAW_PUT_DATA’] = content.freeze
end

   def read_query_params(method)
     case method
       when :get
         read_params_from_query
       when :post
         read_params_from_post
       when :put
         setup_raw_put_data
       when :cmd
         read_from_cmdline
       else # when :head, :delete, :options
         read_params_from_query
     end
   end
 end

end


#2

Here is a newer version of my patch to the bug. In this version, I’m
using RAW_POST_DATA instead of RAW_PUT_DATA. And I call
read_params_from_query right after setup_raw_put_data in
read_query_params to make sure query string is parsed.

I’m still looking for feedback on any better way to properly fix this.
One of the problem of putting the content in env[‘RAW_POST_DATA’] is
that the data could be huge and thus potentially consuming large
amount of memory. I’m thinking of not calling setup_raw_put_data to
pull the data. Instead, I think the controller should pull the data
itself. That should be a more scalable solution.

Thoughts?

class CGI #:nodoc:
  module QueryExtension
    private
    def setup_raw_put_data
      stdinput.binmode if stdinput.respond_to?(:binmode)
      content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) 

|| ‘’
env_table[‘RAW_POST_DATA’] = content.freeze
end

    def read_query_params(method)
      case method
        when :get
          read_params_from_query
        when :post
          read_params_from_post
        when :put
          setup_raw_put_data
          read_params_from_query
        when :cmd
          read_from_cmdline
        else # when :head, :delete, :options
          read_params_from_query
      end
    end
  end
end