class Racknga::Middleware::Cache
This is a middleware that provides page cache.
This stores page contents into a groonga database. A groonga database can access by multi process. It means that your Rack application processes can share the same cache. For example, Passenger runs your Rack application with multi processes.
Cache
key is the request URL by default. It can be customized by env. For example, Racknga::Middleware::PerUserAgentCache
and Racknga::Middleware::JSONP
use it.
This only caches the following responses:
-
200 status response.
-
text/*, */json, */xml or /+xml content type response.
Usage:
use Racnkga::Middleware::Cache, :database_path => "var/cache/db" run YourApplication
@see Racknga::Middleware::PerUserAgentCache
@see Racknga::Middleware::JSONP
@see Racknga::Middleware::Deflater
@see Racknga::CacheDatabase
Constants
- KEY
- START_TIME
Attributes
@return [Racknga::CacheDatabase] the database used
by this middleware.
Public Class Methods
@option options [String] :database_path the database
path to be stored caches.
# File lib/racknga/middleware/cache.rb, line 97 def initialize(application, options={}) @application = application @options = Utils.normalize_options(options || {}) database_path = @options[:database_path] raise ArgumentError, ":database_path is missing" if database_path.nil? @database = CacheDatabase.new(database_path) end
Public Instance Methods
For Rack.
# File lib/racknga/middleware/cache.rb, line 106 def call(environment) request = Rack::Request.new(environment) return @application.call(environment) unless use_cache?(request) age = @database.configuration.age key = normalize_key(environment[KEY] || request.fullpath) environment[START_TIME] = Time.now cache = @database.responses record = cache[key] if record and record.age == age handle_request_with_cache(cache, key, age, record, request) else handle_request(cache, key, age, request) end end
close the cache database.
# File lib/racknga/middleware/cache.rb, line 127 def close_database @database.close_database end
ensures creating cache database.
# File lib/racknga/middleware/cache.rb, line 122 def ensure_database @database.ensure_database end
Private Instance Methods
# File lib/racknga/middleware/cache.rb, line 204 def compute_checksum(status, encoded_headers, encoded_body) checksum = Digest::SHA1.new checksum << status.to_s checksum << ":" checksum << encoded_headers checksum << ":" checksum << encoded_body checksum.hexdigest.force_encoding("ASCII-8BIT") end
# File lib/racknga/middleware/cache.rb, line 160 def handle_request(cache, key, age, request) status, headers, body = @application.call(request.env) if skip_caching_response?(status, headers, body) log("skip", request) return [status, headers, body] end now = Time.now headers = Rack::Utils::HeaderHash.new(headers) headers["Last-Modified"] ||= now.httpdate stringified_body = '' body.each do |data| stringified_body << data end headers = headers.to_hash encoded_headers = headers.to_yaml encoded_body = stringified_body.force_encoding("ASCII-8BIT") cache[key] = { :status => status, :headers => encoded_headers, :body => encoded_body, :checksum => compute_checksum(status, encoded_headers, encoded_body), :age => age, :created_at => now, } body = [stringified_body] log("store", request) [status, headers, body] end
# File lib/racknga/middleware/cache.rb, line 190 def handle_request_with_cache(cache, key, age, record, request) status = record.status headers = record.headers body = record.body checksum = record.checksum unless valid_cache?(status, headers, body, checksum) log("invalid", request) return handle_request(cache, key, age, request) end log("hit", request) [status, YAML.load(headers), [body]] end
# File lib/racknga/middleware/cache.rb, line 220 def log(tag, request) return unless Middleware.const_defined?(:Log) env = request.env logger = env[Middleware::Log::LOGGER] return if logger.nil? start_time = env[START_TIME] runtime = Time.now - start_time logger.log("cache-#{tag}", request.fullpath, :runtime => runtime) end
# File lib/racknga/middleware/cache.rb, line 152 def normalize_key(key) if key.size > 4096 Digest::SHA1.hexdigest(key).force_encoding("ASCII-8BIT") else key end end
# File lib/racknga/middleware/cache.rb, line 136 def skip_caching_response?(status, headers, body) return true if status != 200 headers = Rack::Utils::HeaderHash.new(headers) content_type = headers["Content-Type"] if /\A(\w+)\/([\w.+\-]+)\b/ =~ content_type media_type = $1 sub_type = $2 return false if media_type == "text" return false if sub_type == "json" return false if sub_type == "xml" return false if /\+xml\z/ =~ sub_type end true end
# File lib/racknga/middleware/cache.rb, line 132 def use_cache?(requeust) requeust.get? or requeust.head? end
# File lib/racknga/middleware/cache.rb, line 214 def valid_cache?(status, encoded_headers, encoded_body, checksum) return false if status.nil? or encoded_headers.nil? or encoded_body.nil? return false if checksum.nil? compute_checksum(status, encoded_headers, encoded_body) == checksum end