Search code examples
ruby-on-railsrackruby-on-rails-5

How to accept gzipped requests in a rails 5 application?


In the past I've used this solution, but since Rails 5 deprecated ParamsParser middleware, it doesn't work anymore.


Solution

  • Just add this:

    # config/initializers/middlewares.rb
    require 'compressed_requests'
    
    Rails.application.configure do
      config.middleware.insert_before 0, CompressedRequests
    end
    

    Then add the middleware from this Gist to lib/middleware/compressed_requests.rb:

    class CompressedRequests
      def initialize(app)
        @app = app
      end
    
      def method_handled?(env)
        !!(env['REQUEST_METHOD'] =~ /(POST|PUT)/)
      end
    
      def encoding_handled?(env)
        ['gzip', 'deflate'].include? env['HTTP_CONTENT_ENCODING']
      end
      
      def call(env)
        if method_handled?(env) && encoding_handled?(env)
          extracted = decode(env['rack.input'], env['HTTP_CONTENT_ENCODING'])
    
          env.delete('HTTP_CONTENT_ENCODING')
          env['CONTENT_LENGTH'] = extracted.bytesize
          env['rack.input'] = StringIO.new(extracted)
        end
    
        @app.call(env)
      end
      
      def decode(input, content_encoding)
        case content_encoding
          when 'gzip' then Zlib::GzipReader.new(input).read
          when 'deflate' then Zlib::Inflate.inflate(input.read)
        end
      end
    end
    

    You can test it with:

    # config/routes.rb
    post '/', to: 'welcome#create'
    
    # app/controllers/welcome_controller.rb
    class WelcomeController < ActionController::Base
      def create
        render json: params
      end
    end
    

    And do the request:

    $ curl --data-binary @<(echo "Uncompressed data" | gzip) \
      -H "CONTENT_ENCODING: gzip" \
      localhost:3000
    {"Uncompressed data\n":null,"controller":"welcome","action":"create"}%