rtomayko-rack-cache 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +45 -10
- data/README +14 -9
- data/Rakefile +10 -4
- data/TODO +11 -21
- data/doc/configuration.markdown +8 -0
- data/doc/index.markdown +17 -10
- data/doc/layout.html.erb +1 -0
- data/doc/server.ru +34 -0
- data/lib/rack/cache/config/default.rb +1 -2
- data/lib/rack/cache/core.rb +30 -2
- data/lib/rack/cache/entitystore.rb +29 -6
- data/lib/rack/cache/headers.rb +68 -20
- data/lib/rack/cache/metastore.rb +11 -11
- data/lib/rack/cache/options.rb +11 -1
- data/lib/rack/cache/response.rb +2 -2
- data/rack-cache.gemspec +3 -2
- data/test/cache_test.rb +3 -3
- data/test/context_test.rb +205 -65
- data/test/core_test.rb +8 -8
- data/test/entitystore_test.rb +23 -11
- data/test/environment_headers_test.rb +10 -12
- data/test/headers_test.rb +99 -23
- data/test/metastore_test.rb +27 -17
- data/test/options_test.rb +13 -10
- data/test/spec_setup.rb +13 -7
- metadata +3 -2
data/lib/rack/cache/metastore.rb
CHANGED
@@ -32,18 +32,18 @@ module Rack::Cache
|
|
32
32
|
|
33
33
|
# find a cached entry that matches the request.
|
34
34
|
env = request.env
|
35
|
-
match = entries.detect{
|
36
|
-
if match
|
37
|
-
# TODO what if body doesn't exist in entity store?
|
38
|
-
# reconstruct response object
|
39
|
-
req, res = match
|
40
|
-
status = res['X-Status']
|
41
|
-
body = entity_store.open(res['X-Content-Digest'])
|
42
|
-
response = Rack::Cache::Response.new(status.to_i, res, body)
|
43
|
-
response.activate!
|
35
|
+
match = entries.detect{|req,res| requests_match?(res['Vary'], env, req)}
|
36
|
+
return nil if match.nil?
|
44
37
|
|
45
|
-
|
38
|
+
req, res = match
|
39
|
+
if body = entity_store.open(res['X-Content-Digest'])
|
40
|
+
response = Rack::Cache::Response.new(res['X-Status'].to_i, res, body)
|
41
|
+
response.activate!
|
46
42
|
response
|
43
|
+
else
|
44
|
+
# TODO the metastore referenced an entity that doesn't exist in
|
45
|
+
# the entitystore. we definitely want to return nil but we should
|
46
|
+
# also purge the entry from the meta-store when this is detected.
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
@@ -75,7 +75,7 @@ module Rack::Cache
|
|
75
75
|
(vary == res['Vary']) &&
|
76
76
|
requests_match?(vary, env, stored_env)
|
77
77
|
end
|
78
|
-
entries.unshift [stored_env, response.headers
|
78
|
+
entries.unshift [stored_env, {}.update(response.headers)]
|
79
79
|
write key, entries
|
80
80
|
end
|
81
81
|
|
data/lib/rack/cache/options.rb
CHANGED
@@ -61,6 +61,15 @@ module Rack::Cache
|
|
61
61
|
# Default: 0
|
62
62
|
option_accessor :default_ttl
|
63
63
|
|
64
|
+
# Set of request headers that trigger "private" cache-control behavior
|
65
|
+
# on responses that don't explicitly state whether the response is
|
66
|
+
# public or private via a Cache-Control directive. Applications that use
|
67
|
+
# cookies for authorization may need to add the 'Cookie' header to this
|
68
|
+
# list.
|
69
|
+
#
|
70
|
+
# Default: ['Authorization', 'Cookie']
|
71
|
+
option_accessor :private_headers
|
72
|
+
|
64
73
|
# The underlying options Hash. During initialization (or outside of a
|
65
74
|
# request), this is a default values Hash. During a request, this is the
|
66
75
|
# Rack environment Hash. The default values Hash is merged in underneath
|
@@ -111,7 +120,8 @@ module Rack::Cache
|
|
111
120
|
'rack-cache.storage' => Rack::Cache::Storage.instance,
|
112
121
|
'rack-cache.metastore' => 'heap:/',
|
113
122
|
'rack-cache.entitystore' => 'heap:/',
|
114
|
-
'rack-cache.default_ttl' => 0
|
123
|
+
'rack-cache.default_ttl' => 0,
|
124
|
+
'rack-cache.private_headers' => ['Authorization', 'Cookie']
|
115
125
|
}
|
116
126
|
self.options = options
|
117
127
|
end
|
data/lib/rack/cache/response.rb
CHANGED
@@ -34,7 +34,7 @@ module Rack::Cache
|
|
34
34
|
# and body.
|
35
35
|
def initialize(status, headers, body)
|
36
36
|
@status = status
|
37
|
-
@headers = headers
|
37
|
+
@headers = Rack::Utils::HeaderHash.new(headers)
|
38
38
|
@body = body
|
39
39
|
@now = Time.now
|
40
40
|
@headers['Date'] ||= now.httpdate
|
@@ -62,7 +62,7 @@ module Rack::Cache
|
|
62
62
|
|
63
63
|
# Return the status, headers, and body in a three-tuple.
|
64
64
|
def to_a
|
65
|
-
[status, headers, body]
|
65
|
+
[status, headers.to_hash, body]
|
66
66
|
end
|
67
67
|
|
68
68
|
# Freezes
|
data/rack-cache.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'rack-cache'
|
6
|
-
s.version = '0.
|
7
|
-
s.date = '2008-
|
6
|
+
s.version = '0.3.0'
|
7
|
+
s.date = '2008-12-28'
|
8
8
|
|
9
9
|
s.description = "HTTP Caching for Rack"
|
10
10
|
s.summary = "HTTP Caching for Rack"
|
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
|
|
26
26
|
doc/layout.html.erb
|
27
27
|
doc/license.markdown
|
28
28
|
doc/rack-cache.css
|
29
|
+
doc/server.ru
|
29
30
|
doc/storage.markdown
|
30
31
|
lib/rack/cache.rb
|
31
32
|
lib/rack/cache/config.rb
|
data/test/cache_test.rb
CHANGED
@@ -18,8 +18,8 @@ describe 'Rack::Cache::new' do
|
|
18
18
|
end
|
19
19
|
it 'sets options provided in the options Hash' do
|
20
20
|
object = Rack::Cache.new(@app, :foo => 'bar', 'foo.bar' => 'bling')
|
21
|
-
object.options['foo.bar'].should.
|
22
|
-
object.options['rack-cache.foo'].should.
|
21
|
+
object.options['foo.bar'].should.equal 'bling'
|
22
|
+
object.options['rack-cache.foo'].should.equal 'bar'
|
23
23
|
end
|
24
24
|
it 'takes a block; executes it during initialization' do
|
25
25
|
state, block_scope = 'not invoked', nil
|
@@ -29,7 +29,7 @@ describe 'Rack::Cache::new' do
|
|
29
29
|
state = 'invoked'
|
30
30
|
should.respond_to :on
|
31
31
|
end
|
32
|
-
state.should.
|
32
|
+
state.should.equal 'invoked'
|
33
33
|
object.should.be block_scope
|
34
34
|
end
|
35
35
|
end
|
data/test/context_test.rb
CHANGED
@@ -15,24 +15,58 @@ describe 'Rack::Cache::Context' do
|
|
15
15
|
response.headers.should.not.include 'Age'
|
16
16
|
end
|
17
17
|
|
18
|
-
it '
|
19
|
-
respond_with 200
|
18
|
+
it 'does not cache with Authorization request header and non public response' do
|
19
|
+
respond_with 200, 'Etag' => '"FOO"'
|
20
20
|
get '/', 'HTTP_AUTHORIZATION' => 'basic foobarbaz'
|
21
21
|
|
22
22
|
app.should.be.called
|
23
23
|
response.should.be.ok
|
24
|
-
|
24
|
+
response.headers['Cache-Control'].should.equal 'private'
|
25
|
+
cache.should.a.performed :miss
|
26
|
+
cache.should.a.performed :fetch
|
27
|
+
cache.should.a.not.performed :store
|
28
|
+
cache.should.a.performed :deliver
|
29
|
+
response.headers.should.not.include 'Age'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does cache with Authorization request header and public response' do
|
33
|
+
respond_with 200, 'Cache-Control' => 'public', 'ETag' => '"FOO"'
|
34
|
+
get '/', 'HTTP_AUTHORIZATION' => 'basic foobarbaz'
|
35
|
+
|
36
|
+
app.should.be.called
|
37
|
+
response.should.be.ok
|
38
|
+
cache.should.a.performed :miss
|
39
|
+
cache.should.a.performed :fetch
|
40
|
+
cache.should.a.performed :store
|
41
|
+
response.headers.should.include 'Age'
|
42
|
+
response.headers['Cache-Control'].should.equal 'public'
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not cache with Cookie header and non public response' do
|
46
|
+
respond_with 200, 'Etag' => '"FOO"'
|
47
|
+
get '/', 'HTTP_COOKIE' => 'foo=bar'
|
48
|
+
|
49
|
+
app.should.be.called
|
50
|
+
response.should.be.ok
|
51
|
+
response.headers['Cache-Control'].should.equal 'private'
|
52
|
+
cache.should.a.performed :miss
|
53
|
+
cache.should.a.performed :fetch
|
54
|
+
cache.should.a.not.performed :store
|
55
|
+
cache.should.a.performed :deliver
|
25
56
|
response.headers.should.not.include 'Age'
|
26
57
|
end
|
27
58
|
|
28
|
-
it '
|
59
|
+
it 'does not cache requests with a Cookie header' do
|
29
60
|
respond_with 200
|
30
61
|
get '/', 'HTTP_COOKIE' => 'foo=bar'
|
31
62
|
|
32
63
|
response.should.be.ok
|
33
64
|
app.should.be.called
|
34
|
-
cache.should.a.performed :
|
65
|
+
cache.should.a.performed :miss
|
66
|
+
cache.should.a.performed :fetch
|
67
|
+
cache.should.a.performed :deliver
|
35
68
|
response.headers.should.not.include 'Age'
|
69
|
+
response.headers['Cache-Control'].should.equal 'private'
|
36
70
|
end
|
37
71
|
|
38
72
|
it 'responds with 304 when If-Modified-Since matches Last-Modified' do
|
@@ -47,7 +81,7 @@ describe 'Rack::Cache::Context' do
|
|
47
81
|
get '/',
|
48
82
|
'HTTP_IF_MODIFIED_SINCE' => timestamp
|
49
83
|
app.should.be.called
|
50
|
-
response.status.should.
|
84
|
+
response.status.should.equal 304
|
51
85
|
response.headers.should.not.include 'Content-Length'
|
52
86
|
response.headers.should.not.include 'Content-Type'
|
53
87
|
response.body.should.empty
|
@@ -66,7 +100,7 @@ describe 'Rack::Cache::Context' do
|
|
66
100
|
get '/',
|
67
101
|
'HTTP_IF_NONE_MATCH' => '12345'
|
68
102
|
app.should.be.called
|
69
|
-
response.status.should.
|
103
|
+
response.status.should.equal 304
|
70
104
|
response.headers.should.not.include 'Content-Length'
|
71
105
|
response.headers.should.not.include 'Content-Type'
|
72
106
|
response.headers.should.include 'Etag'
|
@@ -101,7 +135,7 @@ describe 'Rack::Cache::Context' do
|
|
101
135
|
get '/'
|
102
136
|
|
103
137
|
cache.should.a.not.performed :store
|
104
|
-
response.status.should.
|
138
|
+
response.status.should.equal response_code
|
105
139
|
response.headers.should.not.include 'Age'
|
106
140
|
end
|
107
141
|
end
|
@@ -142,13 +176,13 @@ describe 'Rack::Cache::Context' do
|
|
142
176
|
get '/'
|
143
177
|
|
144
178
|
response.should.be.ok
|
145
|
-
response.body.should.
|
179
|
+
response.body.should.equal 'Hello World'
|
146
180
|
response.headers.should.include 'Date'
|
147
181
|
response['Age'].should.not.be.nil
|
148
182
|
response['X-Content-Digest'].should.not.be.nil
|
149
183
|
cache.should.a.performed :miss
|
150
184
|
cache.should.a.performed :store
|
151
|
-
cache.metastore.to_hash.keys.length.should.
|
185
|
+
cache.metastore.to_hash.keys.length.should.equal 1
|
152
186
|
end
|
153
187
|
|
154
188
|
it 'caches responses with a max-age directive' do
|
@@ -156,13 +190,27 @@ describe 'Rack::Cache::Context' do
|
|
156
190
|
get '/'
|
157
191
|
|
158
192
|
response.should.be.ok
|
159
|
-
response.body.should.
|
193
|
+
response.body.should.equal 'Hello World'
|
194
|
+
response.headers.should.include 'Date'
|
195
|
+
response['Age'].should.not.be.nil
|
196
|
+
response['X-Content-Digest'].should.not.be.nil
|
197
|
+
cache.should.a.performed :miss
|
198
|
+
cache.should.a.performed :store
|
199
|
+
cache.metastore.to_hash.keys.length.should.equal 1
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'caches responses with a s-maxage directive' do
|
203
|
+
respond_with 200, 'Cache-Control' => 's-maxage=5'
|
204
|
+
get '/'
|
205
|
+
|
206
|
+
response.should.be.ok
|
207
|
+
response.body.should.equal 'Hello World'
|
160
208
|
response.headers.should.include 'Date'
|
161
209
|
response['Age'].should.not.be.nil
|
162
210
|
response['X-Content-Digest'].should.not.be.nil
|
163
211
|
cache.should.a.performed :miss
|
164
212
|
cache.should.a.performed :store
|
165
|
-
cache.metastore.to_hash.keys.length.should.
|
213
|
+
cache.metastore.to_hash.keys.length.should.equal 1
|
166
214
|
end
|
167
215
|
|
168
216
|
it 'caches responses with a Last-Modified validator but no freshness information' do
|
@@ -170,7 +218,7 @@ describe 'Rack::Cache::Context' do
|
|
170
218
|
get '/'
|
171
219
|
|
172
220
|
response.should.be.ok
|
173
|
-
response.body.should.
|
221
|
+
response.body.should.equal 'Hello World'
|
174
222
|
cache.should.a.performed :miss
|
175
223
|
cache.should.a.performed :store
|
176
224
|
end
|
@@ -180,7 +228,7 @@ describe 'Rack::Cache::Context' do
|
|
180
228
|
get '/'
|
181
229
|
|
182
230
|
response.should.be.ok
|
183
|
-
response.body.should.
|
231
|
+
response.body.should.equal 'Hello World'
|
184
232
|
cache.should.a.performed :miss
|
185
233
|
cache.should.a.performed :store
|
186
234
|
end
|
@@ -196,17 +244,17 @@ describe 'Rack::Cache::Context' do
|
|
196
244
|
response.headers.should.include 'Date'
|
197
245
|
cache.should.a.performed :miss
|
198
246
|
cache.should.a.performed :store
|
199
|
-
response.body.should.
|
247
|
+
response.body.should.equal 'Hello World'
|
200
248
|
|
201
249
|
get '/'
|
202
250
|
response.should.be.ok
|
203
251
|
app.should.not.be.called
|
204
|
-
response['Date'].should.
|
205
|
-
response['Age'].to_i.should.
|
252
|
+
response['Date'].should.equal responses.first['Date']
|
253
|
+
response['Age'].to_i.should.satisfy { |age| age > 0 }
|
206
254
|
response['X-Content-Digest'].should.not.be.nil
|
207
255
|
cache.should.a.performed :hit
|
208
256
|
cache.should.a.not.performed :fetch
|
209
|
-
response.body.should.
|
257
|
+
response.body.should.equal 'Hello World'
|
210
258
|
end
|
211
259
|
|
212
260
|
it 'hits cached response with max-age directive' do
|
@@ -220,17 +268,73 @@ describe 'Rack::Cache::Context' do
|
|
220
268
|
response.headers.should.include 'Date'
|
221
269
|
cache.should.a.performed :miss
|
222
270
|
cache.should.a.performed :store
|
223
|
-
response.body.should.
|
271
|
+
response.body.should.equal 'Hello World'
|
224
272
|
|
225
273
|
get '/'
|
226
274
|
response.should.be.ok
|
227
275
|
app.should.not.be.called
|
228
|
-
response['Date'].should.
|
229
|
-
response['Age'].to_i.should.
|
276
|
+
response['Date'].should.equal responses.first['Date']
|
277
|
+
response['Age'].to_i.should.satisfy { |age| age > 0 }
|
230
278
|
response['X-Content-Digest'].should.not.be.nil
|
231
279
|
cache.should.a.performed :hit
|
232
280
|
cache.should.a.not.performed :fetch
|
233
|
-
response.body.should.
|
281
|
+
response.body.should.equal 'Hello World'
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'hits cached response with s-maxage directive' do
|
285
|
+
respond_with 200,
|
286
|
+
'Date' => (Time.now - 5).httpdate,
|
287
|
+
'Cache-Control' => 's-maxage=10, max-age=0'
|
288
|
+
|
289
|
+
get '/'
|
290
|
+
app.should.be.called
|
291
|
+
response.should.be.ok
|
292
|
+
response.headers.should.include 'Date'
|
293
|
+
cache.should.a.performed :miss
|
294
|
+
cache.should.a.performed :store
|
295
|
+
response.body.should.equal 'Hello World'
|
296
|
+
|
297
|
+
get '/'
|
298
|
+
response.should.be.ok
|
299
|
+
app.should.not.be.called
|
300
|
+
response['Date'].should.equal responses.first['Date']
|
301
|
+
response['Age'].to_i.should.satisfy { |age| age > 0 }
|
302
|
+
response['X-Content-Digest'].should.not.be.nil
|
303
|
+
cache.should.a.performed :hit
|
304
|
+
cache.should.a.not.performed :fetch
|
305
|
+
response.body.should.equal 'Hello World'
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'assigns default_ttl when response has no freshness information' do
|
309
|
+
respond_with 200
|
310
|
+
|
311
|
+
get '/', 'rack-cache.default_ttl' => 10
|
312
|
+
app.should.be.called
|
313
|
+
response.should.be.ok
|
314
|
+
cache.should.a.performed :miss
|
315
|
+
cache.should.a.performed :store
|
316
|
+
response.body.should.equal 'Hello World'
|
317
|
+
response['Cache-Control'].should.include 's-maxage=10'
|
318
|
+
|
319
|
+
get '/', 'rack-cache.default_ttl' => 10
|
320
|
+
response.should.be.ok
|
321
|
+
app.should.not.be.called
|
322
|
+
cache.should.a.performed :hit
|
323
|
+
cache.should.a.not.performed :fetch
|
324
|
+
response.body.should.equal 'Hello World'
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'does not assign default_ttl when response has must-revalidate directive' do
|
328
|
+
respond_with 200,
|
329
|
+
'Cache-Control' => 'must-revalidate'
|
330
|
+
|
331
|
+
get '/', 'rack-cache.default_ttl' => 10
|
332
|
+
app.should.be.called
|
333
|
+
response.should.be.ok
|
334
|
+
cache.should.a.performed :miss
|
335
|
+
cache.should.a.not.performed :store
|
336
|
+
response['Cache-Control'].should.not.include 's-maxage'
|
337
|
+
response.body.should.equal 'Hello World'
|
234
338
|
end
|
235
339
|
|
236
340
|
it 'fetches full response when cache stale and no validators present' do
|
@@ -245,23 +349,23 @@ describe 'Rack::Cache::Context' do
|
|
245
349
|
response.headers.should.include 'Age'
|
246
350
|
cache.should.a.performed :miss
|
247
351
|
cache.should.a.performed :store
|
248
|
-
response.body.should.
|
352
|
+
response.body.should.equal 'Hello World'
|
249
353
|
|
250
354
|
# go in and play around with the cached metadata directly ...
|
251
|
-
cache.metastore.to_hash.values.length.should.
|
355
|
+
cache.metastore.to_hash.values.length.should.equal 1
|
252
356
|
cache.metastore.to_hash.values.first.first[1]['Expires'] = Time.now.httpdate
|
253
357
|
|
254
358
|
# build subsequent request; should be found but miss due to freshness
|
255
359
|
get '/'
|
256
360
|
app.should.be.called
|
257
361
|
response.should.be.ok
|
258
|
-
response['Age'].to_i.should.
|
362
|
+
response['Age'].to_i.should.equal 0
|
259
363
|
response.headers.should.include 'X-Content-Digest'
|
260
364
|
cache.should.a.not.performed :hit
|
261
365
|
cache.should.a.not.performed :miss
|
262
366
|
cache.should.a.performed :fetch
|
263
367
|
cache.should.a.performed :store
|
264
|
-
response.body.should.
|
368
|
+
response.body.should.equal 'Hello World'
|
265
369
|
end
|
266
370
|
|
267
371
|
it 'validates cached responses with Last-Modified and no freshness information' do
|
@@ -280,7 +384,7 @@ describe 'Rack::Cache::Context' do
|
|
280
384
|
response.should.be.ok
|
281
385
|
response.headers.should.include 'Last-Modified'
|
282
386
|
response.headers.should.include 'X-Content-Digest'
|
283
|
-
response.body.should.
|
387
|
+
response.body.should.equal 'Hello World'
|
284
388
|
cache.should.a.performed :miss
|
285
389
|
cache.should.a.performed :store
|
286
390
|
|
@@ -290,9 +394,9 @@ describe 'Rack::Cache::Context' do
|
|
290
394
|
response.should.be.ok
|
291
395
|
response.headers.should.include 'Last-Modified'
|
292
396
|
response.headers.should.include 'X-Content-Digest'
|
293
|
-
response['Age'].to_i.should.
|
294
|
-
response['X-Origin-Status'].should.
|
295
|
-
response.body.should.
|
397
|
+
response['Age'].to_i.should.equal 0
|
398
|
+
response['X-Origin-Status'].should.equal '304'
|
399
|
+
response.body.should.equal 'Hello World'
|
296
400
|
cache.should.a.not.performed :miss
|
297
401
|
cache.should.a.performed :fetch
|
298
402
|
cache.should.a.performed :store
|
@@ -314,7 +418,7 @@ describe 'Rack::Cache::Context' do
|
|
314
418
|
response.should.be.ok
|
315
419
|
response.headers.should.include 'Etag'
|
316
420
|
response.headers.should.include 'X-Content-Digest'
|
317
|
-
response.body.should.
|
421
|
+
response.body.should.equal 'Hello World'
|
318
422
|
cache.should.a.performed :miss
|
319
423
|
cache.should.a.performed :store
|
320
424
|
|
@@ -324,9 +428,9 @@ describe 'Rack::Cache::Context' do
|
|
324
428
|
response.should.be.ok
|
325
429
|
response.headers.should.include 'Etag'
|
326
430
|
response.headers.should.include 'X-Content-Digest'
|
327
|
-
response['Age'].to_i.should.
|
328
|
-
response['X-Origin-Status'].should.
|
329
|
-
response.body.should.
|
431
|
+
response['Age'].to_i.should.equal 0
|
432
|
+
response['X-Origin-Status'].should.equal '304'
|
433
|
+
response.body.should.equal 'Hello World'
|
330
434
|
cache.should.a.not.performed :miss
|
331
435
|
cache.should.a.performed :fetch
|
332
436
|
cache.should.a.performed :store
|
@@ -348,22 +452,58 @@ describe 'Rack::Cache::Context' do
|
|
348
452
|
|
349
453
|
# first request should fetch from backend and store in cache
|
350
454
|
get '/'
|
351
|
-
response.status.should.
|
352
|
-
response.body.should.
|
455
|
+
response.status.should.equal 200
|
456
|
+
response.body.should.equal 'first response'
|
353
457
|
|
354
458
|
# second request is validated, is invalid, and replaces cached entry
|
355
459
|
get '/'
|
356
|
-
response.status.should.
|
357
|
-
response.body.should.
|
460
|
+
response.status.should.equal 200
|
461
|
+
response.body.should.equal 'second response'
|
358
462
|
|
359
463
|
# third respone is validated, valid, and returns cached entry
|
360
464
|
get '/'
|
361
|
-
response.status.should.
|
362
|
-
response.body.should.
|
465
|
+
response.status.should.equal 200
|
466
|
+
response.body.should.equal 'second response'
|
467
|
+
|
468
|
+
count.should.equal 3
|
469
|
+
end
|
470
|
+
|
471
|
+
it 'passes HEAD requests through directly on pass' do
|
472
|
+
respond_with do |req,res|
|
473
|
+
res.status = 200
|
474
|
+
res.body = []
|
475
|
+
req.request_method.should.equal 'HEAD'
|
476
|
+
end
|
477
|
+
|
478
|
+
cache_config do
|
479
|
+
on(:receive) { pass! }
|
480
|
+
end
|
481
|
+
|
482
|
+
head '/'
|
483
|
+
app.should.be.called
|
484
|
+
response.body.should.equal ''
|
485
|
+
end
|
486
|
+
|
487
|
+
it 'uses cache to respond to HEAD requests when fresh' do
|
488
|
+
respond_with do |req,res|
|
489
|
+
res['Cache-Control'] = 'max-age=10'
|
490
|
+
res.body = ['Hello World']
|
491
|
+
req.request_method.should.not.equal 'HEAD'
|
492
|
+
end
|
493
|
+
|
494
|
+
get '/'
|
495
|
+
app.should.be.called
|
496
|
+
response.status.should.equal 200
|
497
|
+
response.body.should.equal 'Hello World'
|
363
498
|
|
364
|
-
|
499
|
+
head '/'
|
500
|
+
app.should.not.be.called
|
501
|
+
response.status.should.equal 200
|
502
|
+
response.body.should.equal ''
|
503
|
+
response['Content-Length'].should.equal 'Hello World'.length.to_s
|
365
504
|
end
|
366
505
|
|
506
|
+
|
367
507
|
describe 'with responses that include a Vary header' do
|
368
508
|
before(:each) do
|
369
509
|
count = 0
|
@@ -380,7 +520,7 @@ describe 'Rack::Cache::Context' do
|
|
380
520
|
'HTTP_ACCEPT' => 'text/html',
|
381
521
|
'HTTP_USER_AGENT' => 'Bob/1.0'
|
382
522
|
response.should.be.ok
|
383
|
-
response.body.should.
|
523
|
+
response.body.should.equal 'Bob/1.0'
|
384
524
|
cache.should.a.performed :miss
|
385
525
|
cache.should.a.performed :store
|
386
526
|
|
@@ -388,7 +528,7 @@ describe 'Rack::Cache::Context' do
|
|
388
528
|
'HTTP_ACCEPT' => 'text/html',
|
389
529
|
'HTTP_USER_AGENT' => 'Bob/1.0'
|
390
530
|
response.should.be.ok
|
391
|
-
response.body.should.
|
531
|
+
response.body.should.equal 'Bob/1.0'
|
392
532
|
cache.should.a.performed :hit
|
393
533
|
cache.should.a.not.performed :fetch
|
394
534
|
response.headers.should.include 'X-Content-Digest'
|
@@ -399,36 +539,36 @@ describe 'Rack::Cache::Context' do
|
|
399
539
|
'HTTP_ACCEPT' => 'text/html',
|
400
540
|
'HTTP_USER_AGENT' => 'Bob/1.0'
|
401
541
|
response.should.be.ok
|
402
|
-
response.body.should.
|
403
|
-
response['X-Response-Count'].should.
|
542
|
+
response.body.should.equal 'Bob/1.0'
|
543
|
+
response['X-Response-Count'].should.equal '1'
|
404
544
|
|
405
545
|
get '/',
|
406
546
|
'HTTP_ACCEPT' => 'text/html',
|
407
547
|
'HTTP_USER_AGENT' => 'Bob/2.0'
|
408
548
|
cache.should.a.performed :miss
|
409
549
|
cache.should.a.performed :store
|
410
|
-
response.body.should.
|
411
|
-
response['X-Response-Count'].should.
|
550
|
+
response.body.should.equal 'Bob/2.0'
|
551
|
+
response['X-Response-Count'].should.equal '2'
|
412
552
|
|
413
553
|
get '/',
|
414
554
|
'HTTP_ACCEPT' => 'text/html',
|
415
555
|
'HTTP_USER_AGENT' => 'Bob/1.0'
|
416
556
|
cache.should.a.performed :hit
|
417
|
-
response.body.should.
|
418
|
-
response['X-Response-Count'].should.
|
557
|
+
response.body.should.equal 'Bob/1.0'
|
558
|
+
response['X-Response-Count'].should.equal '1'
|
419
559
|
|
420
560
|
get '/',
|
421
561
|
'HTTP_ACCEPT' => 'text/html',
|
422
562
|
'HTTP_USER_AGENT' => 'Bob/2.0'
|
423
563
|
cache.should.a.performed :hit
|
424
|
-
response.body.should.
|
425
|
-
response['X-Response-Count'].should.
|
564
|
+
response.body.should.equal 'Bob/2.0'
|
565
|
+
response['X-Response-Count'].should.equal '2'
|
426
566
|
|
427
567
|
get '/',
|
428
568
|
'HTTP_USER_AGENT' => 'Bob/2.0'
|
429
569
|
cache.should.a.performed :miss
|
430
|
-
response.body.should.
|
431
|
-
response['X-Response-Count'].should.
|
570
|
+
response.body.should.equal 'Bob/2.0'
|
571
|
+
response['X-Response-Count'].should.equal '3'
|
432
572
|
end
|
433
573
|
end
|
434
574
|
|
@@ -441,7 +581,7 @@ describe 'Rack::Cache::Context' do
|
|
441
581
|
on(:receive) { error! }
|
442
582
|
end
|
443
583
|
get '/'
|
444
|
-
response.status.should.
|
584
|
+
response.status.should.equal 500
|
445
585
|
response.body.should.be.empty
|
446
586
|
cache.should.a.performed :error
|
447
587
|
end
|
@@ -451,7 +591,7 @@ describe 'Rack::Cache::Context' do
|
|
451
591
|
on(:receive) { error! 505 }
|
452
592
|
end
|
453
593
|
get '/'
|
454
|
-
response.status.should.
|
594
|
+
response.status.should.equal 505
|
455
595
|
end
|
456
596
|
|
457
597
|
it 'sets the status and headers with args: status, Hash' do
|
@@ -459,8 +599,8 @@ describe 'Rack::Cache::Context' do
|
|
459
599
|
on(:receive) { error! 504, 'Content-Type' => 'application/x-foo' }
|
460
600
|
end
|
461
601
|
get '/'
|
462
|
-
response.status.should.
|
463
|
-
response['Content-Type'].should.
|
602
|
+
response.status.should.equal 504
|
603
|
+
response['Content-Type'].should.equal 'application/x-foo'
|
464
604
|
response.body.should.be.empty
|
465
605
|
end
|
466
606
|
|
@@ -469,8 +609,8 @@ describe 'Rack::Cache::Context' do
|
|
469
609
|
on(:receive) { error! 503, 'foo bar baz' }
|
470
610
|
end
|
471
611
|
get '/'
|
472
|
-
response.status.should.
|
473
|
-
response.body.should.
|
612
|
+
response.status.should.equal 503
|
613
|
+
response.body.should.equal 'foo bar baz'
|
474
614
|
end
|
475
615
|
|
476
616
|
it 'sets the status and body with args: status, Array' do
|
@@ -478,8 +618,8 @@ describe 'Rack::Cache::Context' do
|
|
478
618
|
on(:receive) { error! 503, ['foo bar baz'] }
|
479
619
|
end
|
480
620
|
get '/'
|
481
|
-
response.status.should.
|
482
|
-
response.body.should.
|
621
|
+
response.status.should.equal 503
|
622
|
+
response.body.should.equal 'foo bar baz'
|
483
623
|
end
|
484
624
|
|
485
625
|
it 'fires the error event before finishing' do
|
@@ -488,16 +628,16 @@ describe 'Rack::Cache::Context' do
|
|
488
628
|
on(:receive) { error! }
|
489
629
|
on(:error) {
|
490
630
|
fired = true
|
491
|
-
response.status.should.
|
631
|
+
response.status.should.equal 500
|
492
632
|
response['Content-Type'] = 'application/x-foo'
|
493
633
|
response.body = ['overridden response body']
|
494
634
|
}
|
495
635
|
end
|
496
636
|
get '/'
|
497
637
|
fired.should.be true
|
498
|
-
response.status.should.
|
499
|
-
response.body.should.
|
500
|
-
response['Content-Type'].should.
|
638
|
+
response.status.should.equal 500
|
639
|
+
response.body.should.equal 'overridden response body'
|
640
|
+
response['Content-Type'].should.equal 'application/x-foo'
|
501
641
|
end
|
502
642
|
|
503
643
|
end
|