123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- require "zlib"
- require "stringio"
- require "time" # for Time.httpdate
- require 'rack/utils'
- module Rack
- class Deflater
- def initialize(app)
- @app = app
- 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/
- return [status, headers, body]
- end
- request = Request.new(env)
- encoding = Utils.select_best_encoding(%w(gzip deflate identity),
- request.accept_encoding)
- # Set the Vary HTTP header.
- vary = headers["Vary"].to_s.split(",").map { |v| v.strip }
- unless vary.include?("*") || vary.include?("Accept-Encoding")
- headers["Vary"] = vary.push("Accept-Encoding").join(",")
- end
- case encoding
- when "gzip"
- headers['Content-Encoding'] = "gzip"
- headers.delete('Content-Length')
- mtime = headers.key?("Last-Modified") ?
- Time.httpdate(headers["Last-Modified"]) : Time.now
- [status, headers, GzipStream.new(body, mtime)]
- when "deflate"
- headers['Content-Encoding'] = "deflate"
- headers.delete('Content-Length')
- [status, headers, DeflateStream.new(body)]
- when "identity"
- [status, headers, body]
- when nil
- 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]]
- end
- end
- class GzipStream
- def initialize(body, mtime)
- @body = body
- @mtime = mtime
- end
- def each(&block)
- @writer = block
- gzip =::Zlib::GzipWriter.new(self)
- gzip.mtime = @mtime
- @body.each { |part|
- gzip.write(part)
- gzip.flush
- }
- @body.close if @body.respond_to?(:close)
- gzip.close
- @writer = nil
- end
- def write(data)
- @writer.call(data)
- end
- end
- class DeflateStream
- DEFLATE_ARGS = [
- 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
- ]
- def initialize(body)
- @body = body
- end
- def each
- deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
- @body.each { |part| yield deflater.deflate(part, Zlib::SYNC_FLUSH) }
- @body.close if @body.respond_to?(:close)
- yield deflater.finish
- nil
- end
- end
- end
- end
|