Updated README.rdoc again
[feedcatcher.git] / vendor / rails / actionpack / lib / action_controller / vendor / rack-1.0 / rack / deflater.rb
1 require "zlib"
2 require "stringio"
3 require "time" # for Time.httpdate
4 require 'rack/utils'
5
6 module Rack
7 class Deflater
8 def initialize(app)
9 @app = app
10 end
11
12 def call(env)
13 status, headers, body = @app.call(env)
14 headers = Utils::HeaderHash.new(headers)
15
16 # Skip compressing empty entity body responses and responses with
17 # no-transform set.
18 if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
19 headers['Cache-Control'].to_s =~ /\bno-transform\b/
20 return [status, headers, body]
21 end
22
23 request = Request.new(env)
24
25 encoding = Utils.select_best_encoding(%w(gzip deflate identity),
26 request.accept_encoding)
27
28 # Set the Vary HTTP header.
29 vary = headers["Vary"].to_s.split(",").map { |v| v.strip }
30 unless vary.include?("*") || vary.include?("Accept-Encoding")
31 headers["Vary"] = vary.push("Accept-Encoding").join(",")
32 end
33
34 case encoding
35 when "gzip"
36 mtime = headers.key?("Last-Modified") ?
37 Time.httpdate(headers["Last-Modified"]) : Time.now
38 body = self.class.gzip(body, mtime)
39 size = Rack::Utils.bytesize(body)
40 headers = headers.merge("Content-Encoding" => "gzip", "Content-Length" => size.to_s)
41 [status, headers, [body]]
42 when "deflate"
43 body = self.class.deflate(body)
44 size = Rack::Utils.bytesize(body)
45 headers = headers.merge("Content-Encoding" => "deflate", "Content-Length" => size.to_s)
46 [status, headers, [body]]
47 when "identity"
48 [status, headers, body]
49 when nil
50 message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
51 [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]]
52 end
53 end
54
55 def self.gzip(body, mtime)
56 io = StringIO.new
57 gzip = Zlib::GzipWriter.new(io)
58 gzip.mtime = mtime
59
60 # TODO: Add streaming
61 body.each { |part| gzip << part }
62
63 gzip.close
64 return io.string
65 end
66
67 DEFLATE_ARGS = [
68 Zlib::DEFAULT_COMPRESSION,
69 # drop the zlib header which causes both Safari and IE to choke
70 -Zlib::MAX_WBITS,
71 Zlib::DEF_MEM_LEVEL,
72 Zlib::DEFAULT_STRATEGY
73 ]
74
75 # Loosely based on Mongrel's Deflate handler
76 def self.deflate(body)
77 deflater = Zlib::Deflate.new(*DEFLATE_ARGS)
78
79 # TODO: Add streaming
80 body.each { |part| deflater << part }
81
82 return deflater.finish
83 end
84 end
85 end