# Cache Headers # Middleware for configuring HTTP cache policy headers in Rack based web applications. # Apply cache headers to HTTP responses corresponding to requests that match defined # URI patterns. # # Example usage: # # use Rack::CacheHeaders # # Rack::CacheHeaders.configure do |cache| # cache.max_age(/^\/$/, 3600) # cache.private(/^\/search$/) # cache.pragma_no_cache(/^\/search$/) # cache.expires(/^\/search$/, Time.now - 630720000) # cache.expires(/^\/about\/.+$/, "00:00") # end module Rack class CacheHeaders def initialize(app) @app = app end def call(env) result = @app.call(env) if path = Configuration[env['PATH_INFO']] path.each do |policy| header = policy.to_header result[1][header.key] = header.value end end result end def self.configure(&block) yield Configuration end class Configuration def self.max_age(path, duration) (paths[path] ||= []) << MaxAge.new(duration) end def self.expires(path, date) (paths[path] ||= []) << Expires.new(date) end def self.private(path) (paths[path] ||= []) << Private.new end def self.pragma_no_cache(path) (paths[path] ||= []) << PragmaNoCache.new end def self.[](key) key = paths.keys.find { |e| e =~ key } paths[key] if key end def self.paths @paths ||= {} end end class MaxAge def initialize(duration) @duration = duration end def to_header Header.new("Cache-Control", "max-age=#{@duration}, must-revalidate") end end class Expires def initialize(date) @date = date end def to_header if @date.is_a?(Time) Header.new("Expires", @date.httpdate) else d = Time.parse(@date) d = d + (60*60*24) if d < Time.now Header.new("Expires", d.httpdate) end end end class Private def to_header Header.new("Cache-Control", 'private') end end class PragmaNoCache def to_header Header.new('Pragma', 'no-cache') end end class Header < Struct.new(:key, :value);end end end