summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoffer Sawicki <christoffer.sawicki@gmail.com>2008-07-02 00:11:06 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2008-07-06 14:18:30 +0200
commita31ee950d37b123b8b32c792ab448bb57e707431 (patch)
treedead4b045d396fc514d57c8aa6f5e75c17cc1aab
parent0f2dab573bb53d612bd93cc04327bf147909fb95 (diff)
downloadrack-a31ee950d37b123b8b32c792ab448bb57e707431.tar.gz
Implemented Rack::Deflater
-rw-r--r--lib/rack.rb1
-rw-r--r--lib/rack/deflater.rb44
-rw-r--r--test/spec_rack_deflater.rb51
3 files changed, 96 insertions, 0 deletions
diff --git a/lib/rack.rb b/lib/rack.rb
index 607d0f5c..933f5dcb 100644
--- a/lib/rack.rb
+++ b/lib/rack.rb
@@ -30,6 +30,7 @@ module Rack
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"
diff --git a/lib/rack/deflater.rb b/lib/rack/deflater.rb
new file mode 100644
index 00000000..72ba3ed4
--- /dev/null
+++ b/lib/rack/deflater.rb
@@ -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
diff --git a/test/spec_rack_deflater.rb b/test/spec_rack_deflater.rb
new file mode 100644
index 00000000..7fc1c808
--- /dev/null
+++ b/test/spec_rack_deflater.rb
@@ -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