class Rackful::MethodOverride

Middleware that provides method spoofing, like {Rack::MethodOverride}.

If you use this middleware, then clients are allowed to spoof an HTTP method by specifying a ‘_method=…` request parameter, for example `example.com/some_resource?_method=DELETE`.

This can be useful if you want to perform ‘PUT` and `DELETE` requests from within a browser, of when you want to perform a `GET` requests with (too) many parameters, exceeding the maximum URI length in your client or server. In that case, you can put the parameters in a `POST` body, like this:

POST /some_resource HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 123456789
 
param_1=hello&param_2=world&param_3=...

Caveats:

Improvements over Rack::MethodOverride (v1.5.2):

@example Using this middleware

require 'rackful/middleware/method_override'
use Rackful::MethodOverride

Constants

ALLOWED_OVERRIDES
METHOD_OVERRIDE_PARAM_KEY
POST_TO_GET_REQUEST_BODY_MAX_SIZE

Public Class Methods

new( app, options = {} ) click to toggle source

Constructor. @param app [#call] @param options [Hash{Symbol => Mixed}] Configuration options. The following

options are supported:
*   **`:max_size`** the maximum size (in bytes) of the request body of a
    `POST` request that is converted to a `GET` request.
# File lib/rackful/middleware/methodoverride.rb, line 72
def initialize( app, options = {} )
  @app = app
  @max_size = options[:max_size]
end

Public Instance Methods

call(env) click to toggle source
# File lib/rackful/middleware/methodoverride.rb, line 78
def call env
  before_call env
  @app.call env
end

Private Instance Methods

before_call(env) click to toggle source
# File lib/rackful/middleware/methodoverride.rb, line 86
def before_call env
  return unless ['GET', 'POST'].include? env['REQUEST_METHOD']
  new_method = nil
  new_query_string = env['QUERY_STRING'].
  split('&', -1).
  select { |p|
    p = p.split('=', 2)
    if new_method.nil? && METHOD_OVERRIDE_PARAM_KEY == p[0]
      new_method = p[1].upcase
      false
    else
      true
    end
  }.
  join('&')
  if new_method
    if  'GET' == new_method &&
        'POST' == env['REQUEST_METHOD']
      raise HTTP415
        'application/x-www-form-urlencoded' == env['CONTENT_TYPE'] &&
        env['CONTENT_LENGTH'].to_i <= @max_size
      if env.key?('rack.input')
        new_query_string += '&' unless new_query_string.empty?
        new_query_string += env['rack.input'].read
        if  env['rack.input'].respond_to?( :rewind )
            env['rack.input'].rewind
            env['rackful.method_override.input'] = env['rack.input']
        end
        env.delete 'rack.input'
      end
      env.delete 'CONTENT_TYPE'
      env.delete 'CONTENT_LENGTH'
      update_env( env, new_method, new_query_string )
    elsif ALLOWED_OVERRIDES[env['REQUEST_METHOD']].include?( new_method )
      update_env( env, new_method )
    elsif logger = env['rack.logger']
      logger.warn('Rackful::MethodOverride') {
        "Client tried to override request method #{env['REQUEST_METHOD']} with #{new_method} (ignored)."
      }
    else
      STDERR << "warning: Client tried to override request method #{env['REQUEST_METHOD']} with #{new_method} (ignored).\n"
    end
  end
end
update_env(env, new_method, new_query_string = nil) click to toggle source
# File lib/rackful/middleware/methodoverride.rb, line 132
def update_env env, new_method, new_query_string = nil
  unless new_query_string.nil?
    env['rackful.method_override.QUERY_STRING'] = env['QUERY_STRING']
    env['QUERY_STRING'] = new_query_string
  end
  env['rackful.method_override.REQUEST_METHOD'] = env['REQUEST_METHOD']
  env['REQUEST_METHOD'] = new_method
end