An ETag or entity tag, is part of HTTP, the protocol for the World Wide Web. It is one of several mechanisms that HTTP provides for web cache validation, and which allows a client to make conditional requests.
This allows caches to be more efficient, and saves bandwidth, as a web server does not need to send a full response if the content has not changed.
An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL.
If the resource content at that URL ever changes, a new and different ETag is assigned.
Used in this manner ETags are similar to fingerprints, and they can be quickly compared to determine if two versions of a resource are the same or not.
require 'digest/md5'
module Rack
class ETag
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTROL)
@app = app
@cache_control = cache_control
@no_cache_control = no_cache_control
end
def call(env)
status, headers, body = @app.call(env)
if etag_status?(status) && etag_body?(body) && !skip_caching?(headers)
digest, body = digest_body(body)
headers['ETag'] = %("#{digest}") if digest
end
unless headers['Cache-Control']
if digest
headers['Cache-Control'] = @cache_control if @cache_control
else
headers['Cache-Control'] = @no_cache_control if @no_cache_control
end
end
[status, headers, body]
end
private
def etag_status?(status)
status == 200 || status == 201
end
def etag_body?(body)
!body.respond_to?(:to_path)
end
def skip_caching?(headers)
(headers['Cache-Control'] && headers['Cache-Control'].include?('no-cache')) ||
headers.key?('ETag') || headers.key?('Last-Modified')
end
def digest_body(body)
parts = []
digest = nil
body.each do |part|
parts << part
(digest ||= Digest::MD5.new) << part unless part.empty?
end
[digest && digest.hexdigest, parts]
end
end
end
Casiano Rodríguez León