Skip to content

Instantly share code, notes, and snippets.

@relistan
Forked from subdigital/compress_requests.rb
Last active April 18, 2023 23:54
Show Gist options
  • Save relistan/2109707 to your computer and use it in GitHub Desktop.
Save relistan/2109707 to your computer and use it in GitHub Desktop.
Rack Middleware to automatically unzip gzipped/deflated POST data
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
status, headers, response = @app.call(env)
return [status, headers, response]
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
@relistan
Copy link
Author

Rack Middleware to Handle gzip and deflate Content Encodings

This fork has been simplified, clarified and for the common case (gzip) will reduce by one the number of times a StringIO is created and populated with the request body. Also fixes a bug where it would try to handle any content-encoding even though it can only handle two of them. Thanks to subdigital for the original.

@adamonduty
Copy link

Awesome! To get this to work with Rails 3, I had to insert it before ActionDispatch::ParamsParser:

config.middleware.insert_before ActionDispatch::ParamsParser, "CompressedRequests"

@relistan
Copy link
Author

relistan commented Jul 3, 2012

Thanks for adding that!

@asyncrecruiting
Copy link

Can this be used in Rails 2?

@relistan
Copy link
Author

Sorry to miss this question. It should be usable on anything that supports standard Rack middleware, including Rails 2.

@mattbrictson
Copy link

Thanks for this gist! It’s working great.

@mattbrictson
Copy link

I found that for a payload that uses multibyte strings, the content length was not getting set correctly. The solution is to use .bytesize instead of .length.

env['CONTENT_LENGTH'] = extracted.bytesize

Here's my fork with the fix: https://gist.github.com/mbrictson/9106778

@agbodike
Copy link

agbodike commented Dec 4, 2015

It looks like the account name above moved, the updated location is:

https://gist.github.com/mattbrictson/9106778

@plentz
Copy link

plentz commented Oct 25, 2016

config.middleware.insert_before ActionDispatch::ParamsParser, "CompressedRequests"

this does not work with rails 5.

@relistan
Copy link
Author

This is fixed for the CONTENT_LENGTH issue now. I don't know Rails 5 @plentz, so if you knew what needs to go there instead, please post that here.

@sshock
Copy link

sshock commented Apr 28, 2020

It's working for me in rails 6 with config.middleware.insert_after ::Rack::Sendfile, ::CompressedRequests

@bayleedev
Copy link

bayleedev commented Jan 27, 2021

-    status, headers, response = @app.call(env)
-    return [status, headers, response]
+    @app.call(env)

had a similar effect 💚

Used an index in insert_before which worked in rails 5 in all envs.

    config.middleware.insert_before 0, CompressedRequests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment