rtomayko-rack-cache 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|