rack-cache 1.2 → 1.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/test/context_test.rb DELETED
@@ -1,916 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/cache/context'
3
-
4
- describe 'Rack::Cache::Context' do
5
- before { setup_cache_context }
6
- after { teardown_cache_context }
7
-
8
- it 'passes on non-GET/HEAD requests' do
9
- respond_with 200
10
- post '/'
11
-
12
- app.should.be.called
13
- response.should.be.ok
14
- cache.trace.should.include :pass
15
- response.headers.should.not.include 'Age'
16
- end
17
-
18
- it 'passes on rack-cache.force-pass' do
19
- respond_with 200
20
- get '/', {"rack-cache.force-pass" => true}
21
-
22
- app.should.be.called
23
- response.should.be.ok
24
- cache.trace.should == [:pass]
25
- response.headers.should.not.include 'Age'
26
- end
27
-
28
- %w[post put delete].each do |request_method|
29
- it "invalidates on #{request_method} requests" do
30
- respond_with 200
31
- request request_method, '/'
32
-
33
- app.should.be.called
34
- response.should.be.ok
35
- cache.trace.should.include :invalidate
36
- cache.trace.should.include :pass
37
- end
38
- end
39
-
40
- it 'does not cache with Authorization request header and non public response' do
41
- respond_with 200, 'ETag' => '"FOO"'
42
- get '/', 'HTTP_AUTHORIZATION' => 'basic foobarbaz'
43
-
44
- app.should.be.called
45
- response.should.be.ok
46
- response.headers['Cache-Control'].should.equal 'private'
47
- cache.trace.should.include :miss
48
- cache.trace.should.not.include :store
49
- response.headers.should.not.include 'Age'
50
- end
51
-
52
- it 'does cache with Authorization request header and public response' do
53
- respond_with 200, 'Cache-Control' => 'public', 'ETag' => '"FOO"'
54
- get '/', 'HTTP_AUTHORIZATION' => 'basic foobarbaz'
55
-
56
- app.should.be.called
57
- response.should.be.ok
58
- cache.trace.should.include :miss
59
- cache.trace.should.include :store
60
- cache.trace.should.not.include :ignore
61
- response.headers.should.include 'Age'
62
- response.headers['Cache-Control'].should.equal 'public'
63
- end
64
-
65
- it 'does not cache with Cookie header and non public response' do
66
- respond_with 200, 'ETag' => '"FOO"'
67
- get '/', 'HTTP_COOKIE' => 'foo=bar'
68
-
69
- app.should.be.called
70
- response.should.be.ok
71
- response.headers['Cache-Control'].should.equal 'private'
72
- cache.trace.should.include :miss
73
- cache.trace.should.not.include :store
74
- response.headers.should.not.include 'Age'
75
- end
76
-
77
- it 'does not cache requests with a Cookie header' do
78
- respond_with 200
79
- get '/', 'HTTP_COOKIE' => 'foo=bar'
80
-
81
- response.should.be.ok
82
- app.should.be.called
83
- cache.trace.should.include :miss
84
- cache.trace.should.not.include :store
85
- response.headers.should.not.include 'Age'
86
- response.headers['Cache-Control'].should.equal 'private'
87
- end
88
-
89
- it 'does remove Set-Cookie response header from a cacheable response' do
90
- respond_with 200, 'Cache-Control' => 'public', 'ETag' => '"FOO"', 'Set-Cookie' => 'TestCookie=OK'
91
- get '/'
92
-
93
- app.should.be.called
94
- response.should.be.ok
95
- cache.trace.should.include :store
96
- cache.trace.should.include :ignore
97
- response.headers['Set-Cookie'].should.be.nil
98
- end
99
-
100
- it 'does remove all configured ignore_headers from a cacheable response' do
101
- respond_with 200, 'Cache-Control' => 'public', 'ETag' => '"FOO"', 'SET-COOKIE' => 'TestCookie=OK', 'X-Strip-Me' => 'Secret'
102
- get '/', 'rack-cache.ignore_headers' => ['set-cookie', 'x-strip-me']
103
-
104
- app.should.be.called
105
- response.should.be.ok
106
- cache.trace.should.include :store
107
- cache.trace.should.include :ignore
108
- response.headers['Set-Cookie'].should.be.nil
109
- response.headers['x-strip-me'].should.be.nil
110
- end
111
-
112
- it 'does not remove Set-Cookie response header from a private response' do
113
- respond_with 200, 'Cache-Control' => 'private', 'Set-Cookie' => 'TestCookie=OK'
114
- get '/'
115
-
116
- app.should.be.called
117
- response.should.be.ok
118
- cache.trace.should.not.include :store
119
- cache.trace.should.not.include :ignore
120
- response.headers['Set-Cookie'].should.equal 'TestCookie=OK'
121
- end
122
-
123
- it 'responds with 304 when If-Modified-Since matches Last-Modified' do
124
- timestamp = Time.now.httpdate
125
- respond_with do |req,res|
126
- res.status = 200
127
- res['Last-Modified'] = timestamp
128
- res['Content-Type'] = 'text/plain'
129
- res.body = ['Hello World']
130
- end
131
-
132
- get '/',
133
- 'HTTP_IF_MODIFIED_SINCE' => timestamp
134
- app.should.be.called
135
- response.status.should.equal 304
136
- response.original_headers.should.not.include 'Content-Length'
137
- response.original_headers.should.not.include 'Content-Type'
138
- response.body.should.empty
139
- cache.trace.should.include :miss
140
- cache.trace.should.include :store
141
- end
142
-
143
- it 'responds with 304 when If-None-Match matches ETag' do
144
- respond_with do |req,res|
145
- res.status = 200
146
- res['ETag'] = '12345'
147
- res['Content-Type'] = 'text/plain'
148
- res.body = ['Hello World']
149
- end
150
-
151
- get '/',
152
- 'HTTP_IF_NONE_MATCH' => '12345'
153
- app.should.be.called
154
- response.status.should.equal 304
155
- response.original_headers.should.not.include 'Content-Length'
156
- response.original_headers.should.not.include 'Content-Type'
157
- response.headers.should.include 'ETag'
158
- response.body.should.empty
159
- cache.trace.should.include :miss
160
- cache.trace.should.include :store
161
- end
162
-
163
- it 'responds with 304 only if If-None-Match and If-Modified-Since both match' do
164
- timestamp = Time.now
165
-
166
- respond_with do |req,res|
167
- res.status = 200
168
- res['ETag'] = '12345'
169
- res['Last-Modified'] = timestamp.httpdate
170
- res['Content-Type'] = 'text/plain'
171
- res.body = ['Hello World']
172
- end
173
-
174
- # Only etag matches
175
- get '/',
176
- 'HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => (timestamp - 1).httpdate
177
- app.should.be.called
178
- response.status.should.equal 200
179
-
180
- # Only last-modified matches
181
- get '/',
182
- 'HTTP_IF_NONE_MATCH' => '1234', 'HTTP_IF_MODIFIED_SINCE' => timestamp.httpdate
183
- app.should.be.called
184
- response.status.should.equal 200
185
-
186
- # Both matches
187
- get '/',
188
- 'HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => timestamp.httpdate
189
- app.should.be.called
190
- response.status.should.equal 304
191
- end
192
-
193
- it 'validates private responses cached on the client' do
194
- respond_with do |req,res|
195
- etags = req.env['HTTP_IF_NONE_MATCH'].to_s.split(/\s*,\s*/)
196
- if req.env['HTTP_COOKIE'] == 'authenticated'
197
- res['Cache-Control'] = 'private, no-store'
198
- res['ETag'] = '"private tag"'
199
- if etags.include?('"private tag"')
200
- res.status = 304
201
- else
202
- res.status = 200
203
- res['Content-Type'] = 'text/plain'
204
- res.body = ['private data']
205
- end
206
- else
207
- res['ETag'] = '"public tag"'
208
- if etags.include?('"public tag"')
209
- res.status = 304
210
- else
211
- res.status = 200
212
- res['Content-Type'] = 'text/plain'
213
- res.body = ['public data']
214
- end
215
- end
216
- end
217
-
218
- get '/'
219
- app.should.be.called
220
- response.status.should.equal 200
221
- response.headers['ETag'].should == '"public tag"'
222
- response.body.should == 'public data'
223
- cache.trace.should.include :miss
224
- cache.trace.should.include :store
225
-
226
- get '/', 'HTTP_COOKIE' => 'authenticated'
227
- app.should.be.called
228
- response.status.should.equal 200
229
- response.headers['ETag'].should == '"private tag"'
230
- response.body.should == 'private data'
231
- cache.trace.should.include :stale
232
- cache.trace.should.include :invalid
233
- cache.trace.should.not.include :store
234
-
235
- get '/',
236
- 'HTTP_IF_NONE_MATCH' => '"public tag"'
237
- app.should.be.called
238
- response.status.should.equal 304
239
- response.headers['ETag'].should == '"public tag"'
240
- cache.trace.should.include :stale
241
- cache.trace.should.include :valid
242
- cache.trace.should.include :store
243
-
244
- get '/',
245
- 'HTTP_IF_NONE_MATCH' => '"private tag"',
246
- 'HTTP_COOKIE' => 'authenticated'
247
- app.should.be.called
248
- response.status.should.equal 304
249
- response.headers['ETag'].should == '"private tag"'
250
- cache.trace.should.include :valid
251
- cache.trace.should.not.include :store
252
- end
253
-
254
- it 'stores responses when no-cache request directive present' do
255
- respond_with 200, 'Expires' => (Time.now + 5).httpdate
256
-
257
- get '/', 'HTTP_CACHE_CONTROL' => 'no-cache'
258
- response.should.be.ok
259
- cache.trace.should.include :store
260
- response.headers.should.include 'Age'
261
- end
262
-
263
- it 'reloads responses when cache hits but no-cache request directive present ' +
264
- 'when allow_reload is set true' do
265
- count = 0
266
- respond_with 200, 'Cache-Control' => 'max-age=10000' do |req,res|
267
- count+= 1
268
- res.body = (count == 1) ? ['Hello World'] : ['Goodbye World']
269
- end
270
-
271
- get '/'
272
- response.should.be.ok
273
- response.body.should.equal 'Hello World'
274
- cache.trace.should.include :store
275
-
276
- get '/'
277
- response.should.be.ok
278
- response.body.should.equal 'Hello World'
279
- cache.trace.should.include :fresh
280
-
281
- get '/',
282
- 'rack-cache.allow_reload' => true,
283
- 'HTTP_CACHE_CONTROL' => 'no-cache'
284
- response.should.be.ok
285
- response.body.should.equal 'Goodbye World'
286
- cache.trace.should.include :reload
287
- cache.trace.should.include :store
288
- end
289
-
290
- it 'does not reload responses when allow_reload is set false (default)' do
291
- count = 0
292
- respond_with 200, 'Cache-Control' => 'max-age=10000' do |req,res|
293
- count+= 1
294
- res.body = (count == 1) ? ['Hello World'] : ['Goodbye World']
295
- end
296
-
297
- get '/'
298
- response.should.be.ok
299
- response.body.should.equal 'Hello World'
300
- cache.trace.should.include :store
301
-
302
- get '/'
303
- response.should.be.ok
304
- response.body.should.equal 'Hello World'
305
- cache.trace.should.include :fresh
306
-
307
- get '/',
308
- 'rack-cache.allow_reload' => false,
309
- 'HTTP_CACHE_CONTROL' => 'no-cache'
310
- response.should.be.ok
311
- response.body.should.equal 'Hello World'
312
- cache.trace.should.not.include :reload
313
-
314
- # test again without explicitly setting the allow_reload option to false
315
- get '/',
316
- 'HTTP_CACHE_CONTROL' => 'no-cache'
317
- response.should.be.ok
318
- response.body.should.equal 'Hello World'
319
- cache.trace.should.not.include :reload
320
- end
321
-
322
- it 'revalidates fresh cache entry when max-age request directive is exceeded ' +
323
- 'when allow_revalidate option is set true' do
324
- count = 0
325
- respond_with do |req,res|
326
- count+= 1
327
- res['Cache-Control'] = 'max-age=10000'
328
- res['ETag'] = count.to_s
329
- res.body = (count == 1) ? ['Hello World'] : ['Goodbye World']
330
- end
331
-
332
- get '/'
333
- response.should.be.ok
334
- response.body.should.equal 'Hello World'
335
- cache.trace.should.include :store
336
-
337
- get '/'
338
- response.should.be.ok
339
- response.body.should.equal 'Hello World'
340
- cache.trace.should.include :fresh
341
-
342
- get '/',
343
- 'rack-cache.allow_revalidate' => true,
344
- 'HTTP_CACHE_CONTROL' => 'max-age=0'
345
- response.should.be.ok
346
- response.body.should.equal 'Goodbye World'
347
- cache.trace.should.include :stale
348
- cache.trace.should.include :invalid
349
- cache.trace.should.include :store
350
- end
351
-
352
- it 'does not revalidate fresh cache entry when enable_revalidate option is set false (default)' do
353
- count = 0
354
- respond_with do |req,res|
355
- count+= 1
356
- res['Cache-Control'] = 'max-age=10000'
357
- res['ETag'] = count.to_s
358
- res.body = (count == 1) ? ['Hello World'] : ['Goodbye World']
359
- end
360
-
361
- get '/'
362
- response.should.be.ok
363
- response.body.should.equal 'Hello World'
364
- cache.trace.should.include :store
365
-
366
- get '/'
367
- response.should.be.ok
368
- response.body.should.equal 'Hello World'
369
- cache.trace.should.include :fresh
370
-
371
- get '/',
372
- 'rack-cache.allow_revalidate' => false,
373
- 'HTTP_CACHE_CONTROL' => 'max-age=0'
374
- response.should.be.ok
375
- response.body.should.equal 'Hello World'
376
- cache.trace.should.not.include :stale
377
- cache.trace.should.not.include :invalid
378
- cache.trace.should.include :fresh
379
-
380
- # test again without explicitly setting the allow_revalidate option to false
381
- get '/',
382
- 'HTTP_CACHE_CONTROL' => 'max-age=0'
383
- response.should.be.ok
384
- response.body.should.equal 'Hello World'
385
- cache.trace.should.not.include :stale
386
- cache.trace.should.not.include :invalid
387
- cache.trace.should.include :fresh
388
- end
389
- it 'fetches response from backend when cache misses' do
390
- respond_with 200, 'Expires' => (Time.now + 5).httpdate
391
- get '/'
392
-
393
- response.should.be.ok
394
- cache.trace.should.include :miss
395
- response.headers.should.include 'Age'
396
- end
397
-
398
- [(201..202),(204..206),(303..305),(400..403),(405..409),(411..417),(500..505)].each do |range|
399
- range.each do |response_code|
400
- it "does not cache #{response_code} responses" do
401
- respond_with response_code, 'Expires' => (Time.now + 5).httpdate
402
- get '/'
403
-
404
- cache.trace.should.not.include :store
405
- response.status.should.equal response_code
406
- response.headers.should.not.include 'Age'
407
- end
408
- end
409
- end
410
-
411
- it "does not cache responses with explicit no-store directive" do
412
- respond_with 200,
413
- 'Expires' => (Time.now + 5).httpdate,
414
- 'Cache-Control' => 'no-store'
415
- get '/'
416
-
417
- response.should.be.ok
418
- cache.trace.should.not.include :store
419
- response.headers.should.not.include 'Age'
420
- end
421
-
422
- it 'does not cache responses without freshness information or a validator' do
423
- respond_with 200
424
- get '/'
425
-
426
- response.should.be.ok
427
- cache.trace.should.not.include :store
428
- end
429
-
430
- it "caches responses with explicit no-cache directive" do
431
- respond_with 200,
432
- 'Expires' => (Time.now + 5).httpdate,
433
- 'Cache-Control' => 'no-cache'
434
- get '/'
435
-
436
- response.should.be.ok
437
- cache.trace.should.include :store
438
- response.headers.should.include 'Age'
439
- end
440
-
441
- it 'caches responses with an Expiration header' do
442
- respond_with 200, 'Expires' => (Time.now + 5).httpdate
443
- get '/'
444
-
445
- response.should.be.ok
446
- response.body.should.equal 'Hello World'
447
- response.headers.should.include 'Date'
448
- response['Age'].should.not.be.nil
449
- response['X-Content-Digest'].should.not.be.nil
450
- cache.trace.should.include :miss
451
- cache.trace.should.include :store
452
- cache.metastore.to_hash.keys.length.should.equal 1
453
- end
454
-
455
- it 'caches responses with a max-age directive' do
456
- respond_with 200, 'Cache-Control' => 'max-age=5'
457
- get '/'
458
-
459
- response.should.be.ok
460
- response.body.should.equal 'Hello World'
461
- response.headers.should.include 'Date'
462
- response['Age'].should.not.be.nil
463
- response['X-Content-Digest'].should.not.be.nil
464
- cache.trace.should.include :miss
465
- cache.trace.should.include :store
466
- cache.metastore.to_hash.keys.length.should.equal 1
467
- end
468
-
469
- it 'caches responses with a s-maxage directive' do
470
- respond_with 200, 'Cache-Control' => 's-maxage=5'
471
- get '/'
472
-
473
- response.should.be.ok
474
- response.body.should.equal 'Hello World'
475
- response.headers.should.include 'Date'
476
- response['Age'].should.not.be.nil
477
- response['X-Content-Digest'].should.not.be.nil
478
- cache.trace.should.include :miss
479
- cache.trace.should.include :store
480
- cache.metastore.to_hash.keys.length.should.equal 1
481
- end
482
-
483
- it 'caches responses with a Last-Modified validator but no freshness information' do
484
- respond_with 200, 'Last-Modified' => Time.now.httpdate
485
- get '/'
486
-
487
- response.should.be.ok
488
- response.body.should.equal 'Hello World'
489
- cache.trace.should.include :miss
490
- cache.trace.should.include :store
491
- end
492
-
493
- it 'caches responses with an ETag validator but no freshness information' do
494
- respond_with 200, 'ETag' => '"123456"'
495
- get '/'
496
-
497
- response.should.be.ok
498
- response.body.should.equal 'Hello World'
499
- cache.trace.should.include :miss
500
- cache.trace.should.include :store
501
- end
502
-
503
- it 'hits cached response with Expires header' do
504
- respond_with 200,
505
- 'Date' => (Time.now - 5).httpdate,
506
- 'Expires' => (Time.now + 5).httpdate
507
-
508
- get '/'
509
- app.should.be.called
510
- response.should.be.ok
511
- response.headers.should.include 'Date'
512
- cache.trace.should.include :miss
513
- cache.trace.should.include :store
514
- response.body.should.equal 'Hello World'
515
-
516
- get '/'
517
- response.should.be.ok
518
- app.should.not.be.called
519
- response['Date'].should.equal responses.first['Date']
520
- response['Age'].to_i.should.satisfy { |age| age > 0 }
521
- response['X-Content-Digest'].should.not.be.nil
522
- cache.trace.should.include :fresh
523
- cache.trace.should.not.include :store
524
- response.body.should.equal 'Hello World'
525
- end
526
-
527
- it 'hits cached response with max-age directive' do
528
- respond_with 200,
529
- 'Date' => (Time.now - 5).httpdate,
530
- 'Cache-Control' => 'max-age=10'
531
-
532
- get '/'
533
- app.should.be.called
534
- response.should.be.ok
535
- response.headers.should.include 'Date'
536
- cache.trace.should.include :miss
537
- cache.trace.should.include :store
538
- response.body.should.equal 'Hello World'
539
-
540
- get '/'
541
- response.should.be.ok
542
- app.should.not.be.called
543
- response['Date'].should.equal responses.first['Date']
544
- response['Age'].to_i.should.satisfy { |age| age > 0 }
545
- response['X-Content-Digest'].should.not.be.nil
546
- cache.trace.should.include :fresh
547
- cache.trace.should.not.include :store
548
- response.body.should.equal 'Hello World'
549
- end
550
-
551
- it 'hits cached response with s-maxage directive' do
552
- respond_with 200,
553
- 'Date' => (Time.now - 5).httpdate,
554
- 'Cache-Control' => 's-maxage=10, max-age=0'
555
-
556
- get '/'
557
- app.should.be.called
558
- response.should.be.ok
559
- response.headers.should.include 'Date'
560
- cache.trace.should.include :miss
561
- cache.trace.should.include :store
562
- response.body.should.equal 'Hello World'
563
-
564
- get '/'
565
- response.should.be.ok
566
- app.should.not.be.called
567
- response['Date'].should.equal responses.first['Date']
568
- response['Age'].to_i.should.satisfy { |age| age > 0 }
569
- response['X-Content-Digest'].should.not.be.nil
570
- cache.trace.should.include :fresh
571
- cache.trace.should.not.include :store
572
- response.body.should.equal 'Hello World'
573
- end
574
-
575
- it 'assigns default_ttl when response has no freshness information' do
576
- respond_with 200
577
-
578
- get '/', 'rack-cache.default_ttl' => 10
579
- app.should.be.called
580
- response.should.be.ok
581
- cache.trace.should.include :miss
582
- cache.trace.should.include :store
583
- response.body.should.equal 'Hello World'
584
- response['Cache-Control'].should.include 's-maxage=10'
585
-
586
- get '/', 'rack-cache.default_ttl' => 10
587
- response.should.be.ok
588
- app.should.not.be.called
589
- cache.trace.should.include :fresh
590
- cache.trace.should.not.include :store
591
- response.body.should.equal 'Hello World'
592
- end
593
-
594
- it 'does not assign default_ttl when response has must-revalidate directive' do
595
- respond_with 200,
596
- 'Cache-Control' => 'must-revalidate'
597
-
598
- get '/', 'rack-cache.default_ttl' => 10
599
- app.should.be.called
600
- response.should.be.ok
601
- cache.trace.should.include :miss
602
- cache.trace.should.not.include :store
603
- response['Cache-Control'].should.not.include 's-maxage'
604
- response.body.should.equal 'Hello World'
605
- end
606
-
607
- it 'fetches full response when cache stale and no validators present' do
608
- respond_with 200, 'Expires' => (Time.now + 5).httpdate
609
-
610
- # build initial request
611
- get '/'
612
- app.should.be.called
613
- response.should.be.ok
614
- response.headers.should.include 'Date'
615
- response.headers.should.include 'X-Content-Digest'
616
- response.headers.should.include 'Age'
617
- cache.trace.should.include :miss
618
- cache.trace.should.include :store
619
- response.body.should.equal 'Hello World'
620
-
621
- # go in and play around with the cached metadata directly ...
622
- # XXX find some other way to do this
623
- hash = cache.metastore.to_hash
624
- hash.values.length.should.equal 1
625
- entries = Marshal.load(hash.values.first)
626
- entries.length.should.equal 1
627
- req, res = entries.first
628
- res['Expires'] = (Time.now - 1).httpdate
629
- hash[hash.keys.first] = Marshal.dump([[req, res]])
630
-
631
- # build subsequent request; should be found but miss due to freshness
632
- get '/'
633
- app.should.be.called
634
- response.should.be.ok
635
- response['Age'].to_i.should.equal 0
636
- response.headers.should.include 'X-Content-Digest'
637
- cache.trace.should.include :stale
638
- cache.trace.should.not.include :fresh
639
- cache.trace.should.not.include :miss
640
- cache.trace.should.include :store
641
- response.body.should.equal 'Hello World'
642
- end
643
-
644
- it 'validates cached responses with Last-Modified and no freshness information' do
645
- timestamp = Time.now.httpdate
646
- respond_with do |req,res|
647
- res['Last-Modified'] = timestamp
648
- if req.env['HTTP_IF_MODIFIED_SINCE'] == timestamp
649
- res.status = 304
650
- res.body = []
651
- end
652
- end
653
-
654
- # build initial request
655
- get '/'
656
- app.should.be.called
657
- response.should.be.ok
658
- response.headers.should.include 'Last-Modified'
659
- response.headers.should.include 'X-Content-Digest'
660
- response.body.should.equal 'Hello World'
661
- cache.trace.should.include :miss
662
- cache.trace.should.include :store
663
- cache.trace.should.not.include :stale
664
-
665
- # build subsequent request; should be found but miss due to freshness
666
- get '/'
667
- app.should.be.called
668
- response.should.be.ok
669
- response.headers.should.include 'Last-Modified'
670
- response.headers.should.include 'X-Content-Digest'
671
- response['Age'].to_i.should.equal 0
672
- response.body.should.equal 'Hello World'
673
- cache.trace.should.include :stale
674
- cache.trace.should.include :valid
675
- cache.trace.should.include :store
676
- cache.trace.should.not.include :miss
677
- end
678
-
679
- it 'validates cached responses with ETag and no freshness information' do
680
- timestamp = Time.now.httpdate
681
- respond_with do |req,res|
682
- res['ETAG'] = '"12345"'
683
- if req.env['HTTP_IF_NONE_MATCH'] == res['Etag']
684
- res.status = 304
685
- res.body = []
686
- end
687
- end
688
-
689
- # build initial request
690
- get '/'
691
- app.should.be.called
692
- response.should.be.ok
693
- response.headers.should.include 'ETag'
694
- response.headers.should.include 'X-Content-Digest'
695
- response.body.should.equal 'Hello World'
696
- cache.trace.should.include :miss
697
- cache.trace.should.include :store
698
-
699
- # build subsequent request; should be found but miss due to freshness
700
- get '/'
701
- app.should.be.called
702
- response.should.be.ok
703
- response.headers.should.include 'ETag'
704
- response.headers.should.include 'X-Content-Digest'
705
- response['Age'].to_i.should.equal 0
706
- response.body.should.equal 'Hello World'
707
- cache.trace.should.include :stale
708
- cache.trace.should.include :valid
709
- cache.trace.should.include :store
710
- cache.trace.should.not.include :miss
711
- end
712
-
713
- it 'replaces cached responses when validation results in non-304 response' do
714
- timestamp = Time.now.httpdate
715
- count = 0
716
- respond_with do |req,res|
717
- res['Last-Modified'] = timestamp
718
- case (count+=1)
719
- when 1 ; res.body = ['first response']
720
- when 2 ; res.body = ['second response']
721
- when 3
722
- res.body = []
723
- res.status = 304
724
- end
725
- end
726
-
727
- # first request should fetch from backend and store in cache
728
- get '/'
729
- response.status.should.equal 200
730
- response.body.should.equal 'first response'
731
-
732
- # second request is validated, is invalid, and replaces cached entry
733
- get '/'
734
- response.status.should.equal 200
735
- response.body.should.equal 'second response'
736
-
737
- # third respone is validated, valid, and returns cached entry
738
- get '/'
739
- response.status.should.equal 200
740
- response.body.should.equal 'second response'
741
-
742
- count.should.equal 3
743
- end
744
-
745
- it 'passes HEAD requests through directly on pass' do
746
- respond_with do |req,res|
747
- res.status = 200
748
- res.body = []
749
- req.request_method.should.equal 'HEAD'
750
- end
751
-
752
- head '/', 'HTTP_EXPECT' => 'something ...'
753
- app.should.be.called
754
- response.body.should.equal ''
755
- end
756
-
757
- it 'uses cache to respond to HEAD requests when fresh' do
758
- respond_with do |req,res|
759
- res['Cache-Control'] = 'max-age=10'
760
- res.body = ['Hello World']
761
- req.request_method.should.not.equal 'HEAD'
762
- end
763
-
764
- get '/'
765
- app.should.be.called
766
- response.status.should.equal 200
767
- response.body.should.equal 'Hello World'
768
-
769
- head '/'
770
- app.should.not.be.called
771
- response.status.should.equal 200
772
- response.body.should.equal ''
773
- response['Content-Length'].should.equal 'Hello World'.length.to_s
774
- end
775
-
776
- it 'invalidates cached responses on POST' do
777
- respond_with do |req,res|
778
- if req.request_method == 'GET'
779
- res.status = 200
780
- res['Cache-Control'] = 'public, max-age=500'
781
- res.body = ['Hello World']
782
- elsif req.request_method == 'POST'
783
- res.status = 303
784
- res['Location'] = '/'
785
- res.headers.delete('Cache-Control')
786
- res.body = []
787
- end
788
- end
789
-
790
- # build initial request to enter into the cache
791
- get '/'
792
- app.should.be.called
793
- response.should.be.ok
794
- response.body.should.equal 'Hello World'
795
- cache.trace.should.include :miss
796
- cache.trace.should.include :store
797
-
798
- # make sure it is valid
799
- get '/'
800
- app.should.not.called
801
- response.should.be.ok
802
- response.body.should.equal 'Hello World'
803
- cache.trace.should.include :fresh
804
-
805
- # now POST to same URL
806
- post '/'
807
- app.should.be.called
808
- response.should.be.redirect
809
- response['Location'].should.equal '/'
810
- cache.trace.should.include :invalidate
811
- cache.trace.should.include :pass
812
- response.body.should.equal ''
813
-
814
- # now make sure it was actually invalidated
815
- get '/'
816
- app.should.be.called
817
- response.should.be.ok
818
- response.body.should.equal 'Hello World'
819
- cache.trace.should.include :stale
820
- cache.trace.should.include :invalid
821
- cache.trace.should.include :store
822
- end
823
-
824
- describe 'with responses that include a Vary header' do
825
- before do
826
- count = 0
827
- respond_with 200 do |req,res|
828
- res['Vary'] = 'Accept User-Agent Foo'
829
- res['Cache-Control'] = 'max-age=10'
830
- res['X-Response-Count'] = (count+=1).to_s
831
- res.body = [req.env['HTTP_USER_AGENT']]
832
- end
833
- end
834
-
835
- it 'serves from cache when headers match' do
836
- get '/',
837
- 'HTTP_ACCEPT' => 'text/html',
838
- 'HTTP_USER_AGENT' => 'Bob/1.0'
839
- response.should.be.ok
840
- response.body.should.equal 'Bob/1.0'
841
- cache.trace.should.include :miss
842
- cache.trace.should.include :store
843
-
844
- get '/',
845
- 'HTTP_ACCEPT' => 'text/html',
846
- 'HTTP_USER_AGENT' => 'Bob/1.0'
847
- response.should.be.ok
848
- response.body.should.equal 'Bob/1.0'
849
- cache.trace.should.include :fresh
850
- cache.trace.should.not.include :store
851
- response.headers.should.include 'X-Content-Digest'
852
- end
853
-
854
- it 'stores multiple responses when headers differ' do
855
- get '/',
856
- 'HTTP_ACCEPT' => 'text/html',
857
- 'HTTP_USER_AGENT' => 'Bob/1.0'
858
- response.should.be.ok
859
- response.body.should.equal 'Bob/1.0'
860
- response['X-Response-Count'].should.equal '1'
861
-
862
- get '/',
863
- 'HTTP_ACCEPT' => 'text/html',
864
- 'HTTP_USER_AGENT' => 'Bob/2.0'
865
- cache.trace.should.include :miss
866
- cache.trace.should.include :store
867
- response.body.should.equal 'Bob/2.0'
868
- response['X-Response-Count'].should.equal '2'
869
-
870
- get '/',
871
- 'HTTP_ACCEPT' => 'text/html',
872
- 'HTTP_USER_AGENT' => 'Bob/1.0'
873
- cache.trace.should.include :fresh
874
- response.body.should.equal 'Bob/1.0'
875
- response['X-Response-Count'].should.equal '1'
876
-
877
- get '/',
878
- 'HTTP_ACCEPT' => 'text/html',
879
- 'HTTP_USER_AGENT' => 'Bob/2.0'
880
- cache.trace.should.include :fresh
881
- response.body.should.equal 'Bob/2.0'
882
- response['X-Response-Count'].should.equal '2'
883
-
884
- get '/',
885
- 'HTTP_USER_AGENT' => 'Bob/2.0'
886
- cache.trace.should.include :miss
887
- response.body.should.equal 'Bob/2.0'
888
- response['X-Response-Count'].should.equal '3'
889
- end
890
- end
891
-
892
- it 'passes if there was a metastore exception' do
893
- respond_with 200, 'Cache-Control' => 'max-age=10000' do |req,res|
894
- res.body = ['Hello World']
895
- end
896
-
897
- get '/'
898
- response.should.be.ok
899
- response.body.should.equal 'Hello World'
900
- cache.trace.should.include :store
901
-
902
- get '/' do |cache|
903
- cache.meta_def(:metastore) { raise Timeout::Error }
904
- end
905
- response.should.be.ok
906
- response.body.should.equal 'Hello World'
907
- cache.trace.should.include :pass
908
-
909
- post '/' do |cache|
910
- cache.meta_def(:metastore) { raise Timeout::Error }
911
- end
912
- response.should.be.ok
913
- response.body.should.equal 'Hello World'
914
- cache.trace.should.include :pass
915
- end
916
- end