Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

rack middleware parse request parameters twice

#1904

Rack::MethodOverride parses request parameters, then forgets about it. Rails routes parse it again a bit later.

If I make an upload request, I'll end up with pairs of identical RackMultipart files.

Reported by Eugene Pimenov · February 7th, 2009 @ 06:59 AM

State: resolved
Milestone: none
Assigned to: josh josh
Importance: none

Activity

  1. josh
    josh
    • Milestone cleared.
    • Assigned user set to josh

    Can you please test on edge as well. Also, failing unit tests to prove the issue would be appreciated.

    February 7th, 2009 @ 10:14 PM

  2. josh
    josh
    • State changed from new to resolved

    Appears to be fixed by a recent upgrade of rack.

    February 10th, 2009 @ 07:48 PM

  3. Eugene Pimenov
    Eugene Pimenov

    No, it's not.

    Test something like:

    
        Rack::Request.any_instance.expects(:POST).once.returns({})
        @dispatcher.call({"REQUEST_METHOD" => "POST", "rack.input" => StringIO.new(""), "REQUEST_URI" => "/"})
    

    where dispatcher is ActionController::Dispatcher. I'll provide normal test case later.

    The problem because of Rack::MethodOverride has that code

    
            req = Request.new(env)
            method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
              env[HTTP_METHOD_OVERRIDE_HEADER]
    

    It uses Rack::Request, instead of ActionController::Request. ActionController::Request saves itself into rack env, so doesn't parse arguments twice. Rack::Request parses arguments every time POST is invoked.

    To fix that problem locally, I monkey-patched Rack::MethodOverride to use ActionController::Request.

    February 10th, 2009 @ 08:05 PM

  4. josh
    josh
    
        def POST
          if @env["rack.request.form_input"].eql? @env["rack.input"]
            @env["rack.request.form_hash"]
          elsif form_data?
            @env["rack.request.form_input"] = @env["rack.input"]
            unless @env["rack.request.form_hash"] =
                Utils::Multipart.parse_multipart(env)
              form_vars = @env["rack.input"].read
    
              # Fix for Safari Ajax postings that always append \0
              form_vars.sub!(/\0\z/, '')
    
              @env["rack.request.form_vars"] = form_vars
              @env["rack.request.form_hash"] = Utils.parse_query(form_vars)
    
              begin
                @env["rack.input"].rewind if @env["rack.input"].respond_to?(:rewind)
              rescue Errno::ESPIPE
                # Handles exceptions raised by input streams that cannot be rewound
                # such as when using plain CGI under Apache
              end
            end
            @env["rack.request.form_hash"]
          else
            {}
          end
        end
    

    Rack caches the result from Request#POST to @env["rack.request.form_hash"].

    February 10th, 2009 @ 08:30 PM