1 require 'rack/cache'
2 # So we can subclass the storage types
3 require 'rack/cache/storage'
4 require 'rack/cache/metastore'
5 require 'rack/cache/entitystore'
6
7 module Radiant
8 module Cache
9 mattr_accessor :meta_stores, :entity_stores, :use_x_sendfile, :use_x_accel_redirect
10 self.meta_stores ||= []
11 self.entity_stores ||= []
12 self.use_x_sendfile = false
13 self.use_x_accel_redirect = nil
14
15 def self.new(app, options={})
16 self.use_x_sendfile = options.delete(:use_x_sendfile) if options[:use_x_sendfile]
17 self.use_x_accel_redirect = options.delete(:use_x_accel_redirect) if options[:use_x_accel_redirect]
18 Rack::Cache.new(app, {
19 :private_headers => ['Authorization'],
20 :entitystore => "radiant:tmp/cache/entity",
21 :metastore => "radiant:tmp/cache/meta",
22 :verbose => false,
23 :allow_reload => false,
24 :allow_revalidate => false}.merge(options))
25 end
26
27 def self.clear
28 meta_stores.each {|ms| ms.clear }
29 entity_stores.each {|es| es.clear }
30 end
31
32 class EntityStore < Rack::Cache::EntityStore::Disk
33 def initialize(root="#{Rails.root}/tmp/cache/entity")
34 super
35 Radiant::Cache.entity_stores << self
36 end
37
38 def clear
39 Dir[File.join(self.root, "*")].each {|file| FileUtils.rm_rf(file) }
40 end
41
42 def write(body)
43 # Verify that the cache directory exists before attempting to write
44 FileUtils.mkdir_p(self.root, :mode => 0755) unless File.directory?(self.root)
45 super
46 end
47 end
48
49 class MetaStore < Rack::Cache::MetaStore::Disk
50 def initialize(root="#{Rails.root}/tmp/cache/meta")
51 super
52 Radiant::Cache.meta_stores << self
53 end
54
55 def clear
56 Dir[File.join(self.root, "*")].each {|file| FileUtils.rm_rf(file) }
57 end
58
59 def store(request, response, entitystore)
60 # Verify that the cache directory exists before attempting to store
61 FileUtils.mkdir_p(self.root, :mode => 0755) unless File.directory?(self.root)
62 super
63 end
64
65 private
66 def restore_response(hash, body=nil)
67 # Cribbed from the Rack::Cache source
68 status = hash.delete('X-Status').to_i
69 response = Rack::Cache::Response.new(status, hash, body)
70
71 # Add acceleration headers
72 if Radiant::Cache.use_x_sendfile
73 accelerate(response, 'X-Sendfile', File.expand_path(body.path))
74 elsif Radiant::Cache.use_x_accel_redirect
75 virtual_path = File.expand_path(body.path)
76 entity_path = File.expand_path(Radiant::Cache.entity_stores.first.root)
77 virtual_path[entity_path] = Radiant::Cache.use_x_accel_redirect
78 accelerate(response,'X-Accel-Redirect', virtual_path)
79 end
80 response
81 end
82
83 def accelerate(response, header, value)
84 response.headers[header] = value
85 response.body = []
86 response.headers['Content-Length'] = '0'
87 end
88 end
89 end
90 end
91
92 # Rack::Cache will look for a constant matching the uri scheme given to the :metastore and :entitystore options
93 # when initialising the Middleware
94 class Rack::Cache::EntityStore
95 RADIANT = Radiant::Cache::EntityStore
96 end
97
98 class Rack::Cache::MetaStore
99 RADIANT = Radiant::Cache::MetaStore
100 en