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
  • Select an option

  • Save relistan/2109707 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown
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
Copy Markdown

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

relistan commented Jul 3, 2012

Copy link
Copy Markdown
Author

Thanks for adding that!

@asyncrecruiting

Copy link
Copy Markdown

Can this be used in Rails 2?

@relistan

Copy link
Copy Markdown
Author

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

@mattbrictson

Copy link
Copy Markdown

Thanks for this gist! It’s working great.

@mattbrictson

Copy link
Copy Markdown

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

agbodike commented Dec 4, 2015

Copy link
Copy Markdown

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

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

@plentz

plentz commented Oct 25, 2016

Copy link
Copy Markdown
config.middleware.insert_before ActionDispatch::ParamsParser, "CompressedRequests"

this does not work with rails 5.

@relistan

Copy link
Copy Markdown
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

sshock commented Apr 28, 2020

Copy link
Copy Markdown

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

@bayleedev

bayleedev commented Jan 27, 2021

Copy link
Copy Markdown
-    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