Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 65 lines (53 sloc) 1.947 kb
7077d4b @josh Import etag middleware from contrib into core
josh authored
1 require 'digest/md5'
2
3 module Rack
be0e6a0 @josevalim ETag middleware should not be triggered in sendfile bodies.
josevalim authored
4 # Automatically sets the ETag header on all String bodies.
5 #
2a49c15 @raggi Update docs and minor corrections.
raggi authored
6 # The ETag header is skipped if ETag or Last-Modified headers are sent or if
7 # a sendfile body (body.responds_to :to_path) is given (since such cases
8 # should be handled by apache/nginx).
a440ffb @josevalim Etag middleware should not return a digest if body is empty and should a...
josevalim authored
9 #
10 # On initialization, you can pass two parameters: a Cache-Control directive
11 # used when Etag is absent and a directive when it is present. The first
798cce8 @mmb fix typo
mmb authored
12 # defaults to nil, while the second defaults to "max-age=0, private, must-revalidate"
7077d4b @josh Import etag middleware from contrib into core
josh authored
13 class ETag
332d046 @josevalim Ensure the HTTP Cache-Control is set in the Etag middleware if none was ...
josevalim authored
14 DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
15
a440ffb @josevalim Etag middleware should not return a digest if body is empty and should a...
josevalim authored
16 def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTROL)
7077d4b @josh Import etag middleware from contrib into core
josh authored
17 @app = app
332d046 @josevalim Ensure the HTTP Cache-Control is set in the Etag middleware if none was ...
josevalim authored
18 @cache_control = cache_control
a440ffb @josevalim Etag middleware should not return a digest if body is empty and should a...
josevalim authored
19 @no_cache_control = no_cache_control
7077d4b @josh Import etag middleware from contrib into core
josh authored
20 end
21
22 def call(env)
23 status, headers, body = @app.call(env)
24
cd73f8f @josevalim Do not send ETag if no-cache is set.
josevalim authored
25 if etag_status?(status) && etag_body?(body) && !skip_caching?(headers)
f10713c @jeremy Rack::ETag: stream the body digest. Omit to_s on body parts since Rack s...
jeremy authored
26 digest, body = digest_body(body)
a440ffb @josevalim Etag middleware should not return a digest if body is empty and should a...
josevalim authored
27 headers['ETag'] = %("#{digest}") if digest
28 end
29
30 unless headers['Cache-Control']
cefc6de @stephencelis Don't set blank Cache-Control header in Rack::ETag
stephencelis authored
31 if digest
32 headers['Cache-Control'] = @cache_control if @cache_control
33 else
34 headers['Cache-Control'] = @no_cache_control if @no_cache_control
35 end
7077d4b @josh Import etag middleware from contrib into core
josh authored
36 end
f10713c @jeremy Rack::ETag: stream the body digest. Omit to_s on body parts since Rack s...
jeremy authored
37
38 [status, headers, body]
7077d4b @josh Import etag middleware from contrib into core
josh authored
39 end
f10713c @jeremy Rack::ETag: stream the body digest. Omit to_s on body parts since Rack s...
jeremy authored
40
41 private
b986567 @josevalim Should set ETag only if status is 200 or 201.
josevalim authored
42
43 def etag_status?(status)
44 status == 200 || status == 201
45 end
46
a440ffb @josevalim Etag middleware should not return a digest if body is empty and should a...
josevalim authored
47 def etag_body?(body)
48 !body.respond_to?(:to_path)
49 end
50
cd73f8f @josevalim Do not send ETag if no-cache is set.
josevalim authored
51 def skip_caching?(headers)
d127435 @fabiokr Skips Etag even if Cache-Control has a no-cache value together with othe...
fabiokr authored
52 (headers['Cache-Control'] && headers['Cache-Control'].include?('no-cache')) ||
cd73f8f @josevalim Do not send ETag if no-cache is set.
josevalim authored
53 headers.key?('ETag') || headers.key?('Last-Modified')
b986567 @josevalim Should set ETag only if status is 200 or 201.
josevalim authored
54 end
55
f10713c @jeremy Rack::ETag: stream the body digest. Omit to_s on body parts since Rack s...
jeremy authored
56 def digest_body(body)
57 parts = []
a440ffb @josevalim Etag middleware should not return a digest if body is empty and should a...
josevalim authored
58 body.each { |part| parts << part }
59 string_body = parts.join
60 digest = Digest::MD5.hexdigest(string_body) unless string_body.empty?
61 [digest, parts]
f10713c @jeremy Rack::ETag: stream the body digest. Omit to_s on body parts since Rack s...
jeremy authored
62 end
7077d4b @josh Import etag middleware from contrib into core
josh authored
63 end
64 end
Something went wrong with that request. Please try again.