The MetaStore is responsible for storing meta information about a request/response pair keyed by the request's URL.
The meta store keeps a list of request/response pairs for each canonical request URL. A request/response pair is a two element Array of the form:
[request, response]
The request
element is a Hash of Rack environment keys. Only protocol keys (i.e.,
those that start with “HTTP_”) are stored. The response
element is a Hash of cached HTTP response headers for the paired request.
The MetaStore class is abstract and should not be instanstiated directly. Concrete subclasses should implement the protected read, write, and purge methods. Care has been taken to keep these low-level methods dumb and straight-forward to implement.
Concrete MetaStore implementation that stores request/response pairs on disk.
Concrete MetaStore implementation that stores request/response pairs on disk.
Concrete MetaStore implementation that uses a simple Hash to store request/response pairs on the heap.
Concrete MetaStore implementation that uses a simple Hash to store request/response pairs on the heap.
Generate a cache key for the request.
# File lib/rack/cache/metastore.rb, line 88 def cache_key(request) keygen = request.env['rack-cache.cache_key'] || Key keygen.call(request) end
Invalidate all cache entries that match the request.
# File lib/rack/cache/metastore.rb, line 94 def invalidate(request, entity_store) modified = false key = cache_key(request) entries = read(key).map do |req, res| response = restore_response(res) if response.fresh? response.expire! modified = true [req, persist_response(response)] else [req, res] end end write key, entries if modified end
Locate a cached response for the request provided. Returns a Rack::Cache::Response object if the cache hits or nil if no cache entry was found.
# File lib/rack/cache/metastore.rb, line 28 def lookup(request, entity_store) key = cache_key(request) entries = read(key) # bail out if we have nothing cached return nil if entries.empty? # find a cached entry that matches the request. env = request.env match = entries.detect{|req,res| requests_match?(res['Vary'], env, req)} return nil if match.nil? _, res = match if body = entity_store.open(res['X-Content-Digest']) restore_response(res, body) else # TODO the metastore referenced an entity that doesn't exist in # the entitystore. we definitely want to return nil but we should # also purge the entry from the meta-store when this is detected. end end
Write a cache entry to the store under the given key. Existing entries are read and any that match the response are removed. This method calls write with the new list of cache entries.
# File lib/rack/cache/metastore.rb, line 53 def store(request, response, entity_store) key = cache_key(request) stored_env = persist_request(request) # write the response body to the entity store if this is the # original response. if response.headers['X-Content-Digest'].nil? if request.env['rack-cache.use_native_ttl'] && response.fresh? digest, size = entity_store.write(response.body, response.ttl) else digest, size = entity_store.write(response.body) end response.headers['X-Content-Digest'] = digest response.headers['Content-Length'] = size.to_s unless response.headers['Transfer-Encoding'] response.body = entity_store.open(digest) end # read existing cache entries, remove non-varying, and add this one to # the list vary = response.vary entries = read(key).reject do |env,res| (vary == res['Vary']) && requests_match?(vary, env, stored_env) end headers = persist_response(response) headers.delete 'Age' entries.unshift [stored_env, headers] write key, entries key end
Remove all cached entries at the key specified. No error is raised when the key does not exist.
# File lib/rack/cache/metastore.rb, line 163 def purge(key) raise NotImplemented end
Locate all cached request/response pairs that match the specified URL key. The result must be an Array of all cached request/response pairs. An empty Array must be returned if nothing is cached for the specified key.
# File lib/rack/cache/metastore.rb, line 150 def read(key) raise NotImplemented end
Store an Array of request/response pairs for the given key. Concrete implementations should not attempt to filter or concatenate the list in any way.
# File lib/rack/cache/metastore.rb, line 157 def write(key, negotiations) raise NotImplemented end
Generate a SHA1 hex digest for the specified string. This is a simple utility method for meta store implementations.
# File lib/rack/cache/metastore.rb, line 170 def hexdigest(data) Digest::SHA1.hexdigest(data) end
Extract the environment Hash from request
while making any
necessary modifications in preparation for persistence. The Hash returned
must be marshalable.
# File lib/rack/cache/metastore.rb, line 116 def persist_request(request) env = request.env.dup env.reject! { |key,val| key =~ /[^0-9A-Z_]/ || !val.respond_to?(:to_str) } env end
# File lib/rack/cache/metastore.rb, line 129 def persist_response(response) hash = response.headers.to_hash hash['X-Status'] = response.status.to_s hash end
Determine whether the two environment hashes are non-varying based on the vary response header value provided.
# File lib/rack/cache/metastore.rb, line 137 def requests_match?(vary, env1, env2) return true if vary.nil? || vary == '' vary.split(/[\s,]+/).all? do |header| key = "HTTP_#{header.upcase.tr('-', '_')}" env1[key] == env2[key] end end
Converts a stored response hash into a Response object. The caller is responsible for loading and passing the body if needed.
# File lib/rack/cache/metastore.rb, line 124 def restore_response(hash, body=nil) status = hash.delete('X-Status').to_i Rack::Cache::Response.new(status, hash, body) end