Skip to content

Commit

Permalink
Implemented Rack::Deflater
Browse files Browse the repository at this point in the history
  • Loading branch information
qerub authored and leahneukirchen committed Jul 6, 2008
1 parent 0f2dab5 commit a31ee95
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/rack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def self.release
autoload :Cascade, "rack/cascade"
autoload :CommonLogger, "rack/commonlogger"
autoload :File, "rack/file"
autoload :Deflater, "rack/deflater"
autoload :Directory, "rack/directory"
autoload :ForwardRequest, "rack/recursive"
autoload :Handler, "rack/handler"
Expand Down
44 changes: 44 additions & 0 deletions lib/rack/deflater.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require "zlib"

module Rack

class Deflater
def initialize(app)
@app = app
end

def call(env)
status, headers, body = @app.call(env)

request = Request.new(env)
encoding = Utils.select_best_encoding(%w(deflate identity), request.accept_encoding)

case encoding
when "deflate"
[status, headers.merge("Content-Encoding" => "deflate"), self.class.deflate(body)]
when "identity"
[status, headers, body]
when nil
# TODO: Add Content-Type
[406, {}, "..."]
end
end

# Loosely based on Mongrel's Deflate handler
def self.deflate(body)
deflater = Zlib::Deflate.new(
Zlib::DEFAULT_COMPRESSION,
# drop the zlib header which causes both Safari and IE to choke
-Zlib::MAX_WBITS,
Zlib::DEF_MEM_LEVEL,
Zlib::DEFAULT_STRATEGY)

# TODO: Add streaming
# TODO: Consider all part types
body.each { |part| deflater << part }

return deflater.finish
end
end

end
51 changes: 51 additions & 0 deletions test/spec_rack_deflater.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require 'test/spec'

require 'rack/mock'
require 'rack/deflater'
require 'stringio'

context "Rack::Deflater" do
def build_response(body, accept_encoding)
app = lambda { |env| [200, {}, body] }
request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => accept_encoding)
response = Rack::Deflater.new(app).call(request)

return response
end

specify "should be able to deflate bodies that respond to each" do
body = Object.new
class << body; def each; yield("foo"); yield("bar"); end; end

response = build_response(body, "deflate")

response[0].should.equal(200)
response[1].should.equal({ "Content-Encoding" => "deflate" })
response[2].to_s.should.equal("K\313\317OJ,\002\000")
end

# TODO: This is really just a special case of the above...
specify "should be able to deflate String bodies" do
response = build_response("Hello world!", "deflate")

response[0].should.equal(200)
response[1].should.equal({ "Content-Encoding" => "deflate" })
response[2].to_s.should.equal("\363H\315\311\311W(\317/\312IQ\004\000")
end

specify "should be able to fallback to no deflation" do
response = build_response("Hello world!", "superzip")

response[0].should.equal(200)
response[1].should.equal({})
response[2].should.equal("Hello world!")
end

specify "should handle the lack of an acceptable encoding" do
response = build_response("Hello world!", "identity;q=0")

response[0].should.equal(406)
response[1].should.equal({})
# response[2].should.equal("...")
end
end

0 comments on commit a31ee95

Please sign in to comment.