lib/rack/deflater.rb in rack-1.5.5 vs lib/rack/deflater.rb in rack-1.6.0.beta
- old
+ new
@@ -15,23 +15,30 @@
# The middleware automatically detects when compression is supported
# and allowed. For example no transformation is made when a cache
# directive of 'no-transform' is present, or when the response status
# code is one that doesn't allow an entity body.
class Deflater
- def initialize(app)
+ ##
+ # Creates Rack::Deflater middleware.
+ #
+ # [app] rack app instance
+ # [options] hash of deflater options, i.e.
+ # 'if' - a lambda enabling / disabling deflation based on returned boolean value
+ # e.g use Rack::Deflater, :if => lambda { |env, status, headers, body| body.length > 512 }
+ # 'include' - a list of content types that should be compressed
+ def initialize(app, options = {})
@app = app
+
+ @condition = options[:if]
+ @compressible_types = options[:include]
end
def call(env)
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
- # Skip compressing empty entity body responses and responses with
- # no-transform set.
- if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
- headers['Cache-Control'].to_s =~ /\bno-transform\b/ ||
- (headers['Content-Encoding'] && headers['Content-Encoding'] !~ /\bidentity\b/)
+ unless should_deflate?(env, status, headers, body)
return [status, headers, body]
end
request = Request.new(env)
@@ -56,20 +63,21 @@
headers.delete('Content-Length')
[status, headers, DeflateStream.new(body)]
when "identity"
[status, headers, body]
when nil
- body.close if body.respond_to?(:close)
message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
- [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]]
+ bp = Rack::BodyProxy.new([message]) { body.close if body.respond_to?(:close) }
+ [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, bp]
end
end
class GzipStream
def initialize(body, mtime)
@body = body
@mtime = mtime
+ @closed = false
end
def each(&block)
@writer = block
gzip =::Zlib::GzipWriter.new(self)
@@ -77,18 +85,23 @@
@body.each { |part|
gzip.write(part)
gzip.flush
}
ensure
- @body.close if @body.respond_to?(:close)
gzip.close
@writer = nil
end
def write(data)
@writer.call(data)
end
+
+ def close
+ return if @closed
+ @closed = true
+ @body.close if @body.respond_to?(:close)
+ end
end
class DeflateStream
DEFLATE_ARGS = [
Zlib::DEFAULT_COMPRESSION,
@@ -98,19 +111,45 @@
Zlib::DEFAULT_STRATEGY
]
def initialize(body)
@body = body
+ @closed = false
end
def each
- deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
- @body.each { |part| yield deflater.deflate(part, Zlib::SYNC_FLUSH) }
- yield deflater.finish
+ deflator = ::Zlib::Deflate.new(*DEFLATE_ARGS)
+ @body.each { |part| yield deflator.deflate(part, Zlib::SYNC_FLUSH) }
+ yield deflator.finish
nil
ensure
+ deflator.close
+ end
+
+ def close
+ return if @closed
+ @closed = true
@body.close if @body.respond_to?(:close)
- deflater.close
end
+ end
+
+ private
+
+ def should_deflate?(env, status, headers, body)
+ # Skip compressing empty entity body responses and responses with
+ # no-transform set.
+ if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
+ headers['Cache-Control'].to_s =~ /\bno-transform\b/ ||
+ (headers['Content-Encoding'] && headers['Content-Encoding'] !~ /\bidentity\b/)
+ return false
+ end
+
+ # Skip if @compressible_types are given and does not include request's content type
+ return false if @compressible_types && !(headers.has_key?('Content-Type') && @compressible_types.include?(headers['Content-Type'][/[^;]*/]))
+
+ # Skip if @condition lambda is given and evaluates to false
+ return false if @condition && !@condition.call(env, status, headers, body)
+
+ true
end
end
end