rack-cache 1.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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