Artsy

10x Rack and Rails Output Compression With Rack::Deflater

You can quickly reduce the amount of data transferred from your Rack or Rails application with Rack::Deflater. Anecdotal evidence shows a reduction from a 50Kb JSON response into about 6Kb. It may be a huge deal for your mobile clients.

For a Rails application, modify config/application.rb or config/environment.rb.

config/application.rb
1
2
3
Acme::Application.configure do
  config.middleware.use Rack::Deflater
end

For a Rack application, add the middleware in config.ru.

config.ru
1
2
use Rack::Deflater
run Acme::Instance

Note that the order of the middleware is very important. For example, we also use Rack::JSONP that adds automatic JSONP support to our API. It must be invoked before Rack::Deflater or it will attempt to wrap compressed content. Rack middleware is executed in reverse order [source].

config/application.rb
1
2
  config.middleware.use Rack::Deflater
  config.middleware.use Rack::JSONP

A couple of handy RSpec tests to add to your application. You will need to modify this code with a valid API path and expected response.

spec/api/rack_deflater_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'spec_helper'

describe Rack::Deflater do
  it "produces an identical eTag whether content is deflated or not" do
    get "/api/acme"
    response.headers["Content-Encoding"].should be_nil
    etag = response.headers["Etag"]
    content_length = response.headers["Content-Length"].to_i
    get "/api/acme", {}, { "HTTP_ACCEPT_ENCODING" => "gzip" }
    response.headers["Etag"].should == etag
    response.headers["Content-Length"].to_i.should_not == content_length
    response.headers["Content-Encoding"].should == "gzip"
  end
  it "deflates JSONP content" do
    get "/api/acme?callback=parseResponse", {}, { "HTTP_ACCEPT_ENCODING" => "deflate" }
    response.headers["Content-Encoding"].should == "deflate"
    inflated_response_body = Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(response.body.to_s)
    inflated_response_body.should == "parseResponse(...)"
  end
end

Comments