Wrapping Rack Middleware to Exclude Certain URLs (For Rails Streaming Responses)
Another one of the tweaks I had to make to accomodate Rails streaming responses was ensuring that the Rack::Deflater
middleware didn’t run on my app’s streaming actions.
The Deflater middleware gzips your responses before delivering them to the client. This is useful to run on the Heroku Cedar stack because this facility is otherwise not provided. There’s a big gotcha, though: the middleware breaks Rails HTTP streaming responses. There’s a good explanation of this on StackOverflow, as well as a monkey patch to ActionController::Streaming
that corrects the behaviour.
I took an alternative, simpler approach. I extended the middleware with support for excluding a URL pattern. This way, I can just disable it on the few actions where I know I am streaming responses. Here’s how it looks:
module Rack
class DeflaterWithExclusions < Deflater
def initialize(app, options = {})
@app = app
@exclude = options[:exclude]
end
def call(env)
if @exclude && @exclude.call(env)
@app.call(env)
else
super(env)
end
end
end
end
And here’s how the extended middleware is included, in config.ru
:
use Rack::DeflaterWithExclusions, :exclude => proc { |env|
env['PATH_INFO'] == '/order/purchase'
}
Using an executable proc for the :exclude
argument is useful because it then allows for far more sophisticated URL matching than simple string equality (in production, I actually check against a couple of regular expressions).
Wrapping the middleware in this way has worked well. We get to keep it for the bulk of the app, where it’s useful, without it interfering with the parts that are more touchy. It would be nice to see this kind of :exclude
option handling become more common among Rack middlewares.