rtomayko-rack-cache 0.3.0 → 0.3.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +41 -0
- data/README +0 -1
- data/TODO +14 -10
- data/doc/configuration.markdown +7 -153
- data/doc/index.markdown +1 -3
- data/example/sinatra/app.rb +25 -0
- data/example/sinatra/views/index.erb +44 -0
- data/lib/rack/cache.rb +5 -11
- data/lib/rack/cache/cachecontrol.rb +193 -0
- data/lib/rack/cache/context.rb +188 -51
- data/lib/rack/cache/entitystore.rb +10 -4
- data/lib/rack/cache/key.rb +52 -0
- data/lib/rack/cache/metastore.rb +52 -16
- data/lib/rack/cache/options.rb +29 -13
- data/lib/rack/cache/request.rb +11 -15
- data/lib/rack/cache/response.rb +221 -30
- data/lib/rack/cache/storage.rb +1 -2
- data/rack-cache.gemspec +9 -14
- data/test/cache_test.rb +4 -1
- data/test/cachecontrol_test.rb +139 -0
- data/test/context_test.rb +198 -169
- data/test/entitystore_test.rb +12 -11
- data/test/key_test.rb +50 -0
- data/test/metastore_test.rb +57 -14
- data/test/options_test.rb +11 -0
- data/test/request_test.rb +19 -0
- data/test/response_test.rb +164 -23
- data/test/spec_setup.rb +6 -0
- metadata +13 -19
- data/lib/rack/cache/config.rb +0 -65
- data/lib/rack/cache/config/busters.rb +0 -16
- data/lib/rack/cache/config/default.rb +0 -133
- data/lib/rack/cache/config/no-cache.rb +0 -13
- data/lib/rack/cache/core.rb +0 -299
- data/lib/rack/cache/headers.rb +0 -325
- data/lib/rack/utils/environment_headers.rb +0 -78
- data/test/config_test.rb +0 -66
- data/test/core_test.rb +0 -84
- data/test/environment_headers_test.rb +0 -69
- data/test/headers_test.rb +0 -298
- data/test/logging_test.rb +0 -45
data/test/core_test.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
require "#{File.dirname(__FILE__)}/spec_setup"
|
2
|
-
require 'rack/cache/core'
|
3
|
-
|
4
|
-
class MockCore
|
5
|
-
include Rack::Cache::Core
|
6
|
-
alias_method :initialize, :initialize_core
|
7
|
-
public :transition, :trigger, :events
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'Rack::Cache::Core' do
|
11
|
-
before :each do
|
12
|
-
@core = MockCore.new
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'has events after instantiation' do
|
16
|
-
@core.events.should.respond_to :[]
|
17
|
-
end
|
18
|
-
it 'defines and triggers event handlers' do
|
19
|
-
executed = false
|
20
|
-
@core.on(:foo) { executed = true }
|
21
|
-
@core.trigger :foo
|
22
|
-
executed.should.be true
|
23
|
-
end
|
24
|
-
it 'executes multiple handlers in LIFO order' do
|
25
|
-
x = 'nothing executed'
|
26
|
-
@core.on :foo do
|
27
|
-
x.should.equal 'bottom executed'
|
28
|
-
x = 'top executed'
|
29
|
-
end
|
30
|
-
@core.on :foo do
|
31
|
-
x.should.equal 'nothing executed'
|
32
|
-
x = 'bottom executed'
|
33
|
-
end
|
34
|
-
@core.trigger :foo
|
35
|
-
x.should.equal 'top executed'
|
36
|
-
end
|
37
|
-
it 'records event execution history' do
|
38
|
-
@core.on(:foo) {}
|
39
|
-
@core.trigger :foo
|
40
|
-
@core.should.a.performed :foo
|
41
|
-
end
|
42
|
-
it 'raises an exception when asked to perform an unknown event' do
|
43
|
-
assert_raises NameError do
|
44
|
-
@core.trigger :foo
|
45
|
-
end
|
46
|
-
end
|
47
|
-
it 'raises an exception when asked to transition to an unknown event' do
|
48
|
-
@core.on(:bling) {}
|
49
|
-
@core.on(:foo) { throw(:transition, [:bling]) }
|
50
|
-
lambda { @core.transition(from=:foo, to=[:bar, :baz]) }.
|
51
|
-
should.raise Rack::Cache::IllegalTransition
|
52
|
-
end
|
53
|
-
it 'passes transition arguments to handlers' do
|
54
|
-
passed = nil
|
55
|
-
@core.meta_def(:perform_bar) do |*args|
|
56
|
-
passed = args
|
57
|
-
'hi'
|
58
|
-
end
|
59
|
-
@core.on(:bar) {}
|
60
|
-
@core.on(:foo) { throw(:transition, [:bar, 1, 2, 3]) }
|
61
|
-
result = @core.transition(from=:foo, to=[:bar])
|
62
|
-
passed.should.equal [1,2,3]
|
63
|
-
result.should.equal 'hi'
|
64
|
-
end
|
65
|
-
it 'fully transitions out of handlers when the next event is invoked' do
|
66
|
-
x = []
|
67
|
-
@core.on(:foo) {
|
68
|
-
x << 'in foo, before transitioning to bar'
|
69
|
-
throw(:transition, [:bar])
|
70
|
-
x << 'in foo, after transitioning to bar'
|
71
|
-
}
|
72
|
-
@core.on(:bar) { x << 'in bar' }
|
73
|
-
@core.trigger(:foo).should.equal [:bar]
|
74
|
-
@core.trigger(:bar).should.be.nil
|
75
|
-
x.should.equal [
|
76
|
-
'in foo, before transitioning to bar',
|
77
|
-
'in bar'
|
78
|
-
]
|
79
|
-
end
|
80
|
-
it 'returns the transition event name' do
|
81
|
-
@core.on(:foo) { throw(:transition, [:bar]) }
|
82
|
-
@core.trigger(:foo).should.equal [:bar]
|
83
|
-
end
|
84
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
require "#{File.dirname(__FILE__)}/spec_setup"
|
2
|
-
require 'rack/utils/environment_headers'
|
3
|
-
|
4
|
-
describe 'Rack::Utils::EnvironmentHeaders' do
|
5
|
-
|
6
|
-
before :each do
|
7
|
-
@now = Time.now.httpdate
|
8
|
-
@env = {
|
9
|
-
'CONTENT_TYPE' => 'text/plain',
|
10
|
-
'CONTENT_LENGTH' => '0x1A4',
|
11
|
-
'HTTP_X_FOO' => 'BAR',
|
12
|
-
'HTTP_IF_MODIFIED_SINCE' => @now,
|
13
|
-
'rack.run_once' => true
|
14
|
-
}
|
15
|
-
@h = Rack::Utils::EnvironmentHeaders.new(@env)
|
16
|
-
end
|
17
|
-
|
18
|
-
after(:each) {
|
19
|
-
@env, @h = nil, nil
|
20
|
-
}
|
21
|
-
|
22
|
-
it 'retrieves headers with #[]' do
|
23
|
-
@h.should.respond_to :[]
|
24
|
-
@h['X-Foo'].should.equal 'BAR'
|
25
|
-
@h['If-Modified-Since'].should.equal @now
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'sets headers with #[]=' do
|
29
|
-
@h.should.respond_to :[]=
|
30
|
-
@h['X-Foo'] = 'BAZZLE'
|
31
|
-
@h['X-Foo'].should.equal 'BAZZLE'
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'sets values on the underlying environment hash' do
|
35
|
-
@h['X-Something-Else'] = 'FOO'
|
36
|
-
@env['HTTP_X_SOMETHING_ELSE'].should.equal 'FOO'
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'handles Content-Type special case' do
|
40
|
-
@h['Content-Type'].should.equal 'text/plain'
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'handles Content-Length special case' do
|
44
|
-
@h['Content-Length'].should.equal '0x1A4'
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'implements #include? with RFC 2616 header name' do
|
48
|
-
@h.should.include 'If-Modified-Since'
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'deletes underlying env entries' do
|
52
|
-
@h.delete('X-Foo')
|
53
|
-
@env.should.not.include? 'HTTP_X_FOO'
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'returns the underlying environment hash with #to_env' do
|
57
|
-
@h.to_env.should.be @env
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'iterates over all headers with #each' do
|
61
|
-
hash = {}
|
62
|
-
@h.each { |name,value| hash[name] = value }
|
63
|
-
hash.should.equal 'Content-Type' => 'text/plain',
|
64
|
-
'Content-Length' => '0x1A4',
|
65
|
-
'X-Foo' => 'BAR',
|
66
|
-
'If-Modified-Since' => @now
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
data/test/headers_test.rb
DELETED
@@ -1,298 +0,0 @@
|
|
1
|
-
require "#{File.dirname(__FILE__)}/spec_setup"
|
2
|
-
|
3
|
-
class MockResponse < Rack::MockResponse
|
4
|
-
include Rack::Cache::Headers
|
5
|
-
include Rack::Cache::ResponseHeaders
|
6
|
-
public :now
|
7
|
-
end
|
8
|
-
|
9
|
-
describe 'Rack::Cache::Headers' do
|
10
|
-
before :each do
|
11
|
-
@now = Time.httpdate(Time.now.httpdate)
|
12
|
-
@res = MockResponse.new(200, {'Date' => @now.httpdate}, '')
|
13
|
-
@one_hour_ago = Time.httpdate((Time.now - (60**2)).httpdate)
|
14
|
-
end
|
15
|
-
after :each do
|
16
|
-
@now, @res, @one_hour_ago = nil
|
17
|
-
end
|
18
|
-
|
19
|
-
describe '#cache_control' do
|
20
|
-
it 'handles single name=value pair' do
|
21
|
-
@res.headers['Cache-Control'] = 'max-age=600'
|
22
|
-
@res.cache_control['max-age'].should.equal '600'
|
23
|
-
end
|
24
|
-
it 'handles multiple name=value pairs' do
|
25
|
-
@res.headers['Cache-Control'] = 'max-age=600, max-stale=300, min-fresh=570'
|
26
|
-
@res.cache_control['max-age'].should.equal '600'
|
27
|
-
@res.cache_control['max-stale'].should.equal '300'
|
28
|
-
@res.cache_control['min-fresh'].should.equal '570'
|
29
|
-
end
|
30
|
-
it 'handles a single flag value' do
|
31
|
-
@res.headers['Cache-Control'] = 'no-cache'
|
32
|
-
@res.cache_control.should.include 'no-cache'
|
33
|
-
@res.cache_control['no-cache'].should.be true
|
34
|
-
end
|
35
|
-
it 'handles a bunch of all kinds of stuff' do
|
36
|
-
@res.headers['Cache-Control'] = 'max-age=600,must-revalidate,min-fresh=3000,foo=bar,baz'
|
37
|
-
@res.cache_control['max-age'].should.equal '600'
|
38
|
-
@res.cache_control['must-revalidate'].should.be true
|
39
|
-
@res.cache_control['min-fresh'].should.equal '3000'
|
40
|
-
@res.cache_control['foo'].should.equal 'bar'
|
41
|
-
@res.cache_control['baz'].should.be true
|
42
|
-
end
|
43
|
-
it 'removes the header when given an empty hash' do
|
44
|
-
@res.headers['Cache-Control'] = 'max-age=600, must-revalidate'
|
45
|
-
@res.cache_control['max-age'].should.equal '600'
|
46
|
-
@res.cache_control = {}
|
47
|
-
@res.headers.should.not.include 'Cache-Control'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe 'Rack::Cache::ResponseHeaders' do
|
53
|
-
before :each do
|
54
|
-
@now = Time.httpdate(Time.now.httpdate)
|
55
|
-
@one_hour_ago = Time.httpdate((Time.now - (60**2)).httpdate)
|
56
|
-
@one_hour_later = Time.httpdate((Time.now + (60**2)).httpdate)
|
57
|
-
@res = MockResponse.new(200, {'Date' => @now.httpdate}, '')
|
58
|
-
end
|
59
|
-
after :each do
|
60
|
-
@now, @res, @one_hour_ago = nil
|
61
|
-
end
|
62
|
-
|
63
|
-
describe '#validateable?' do
|
64
|
-
it 'is true when Last-Modified header present' do
|
65
|
-
@res = MockResponse.new(200, { 'Last-Modified' => @one_hour_ago.httpdate }, '')
|
66
|
-
@res.extend Rack::Cache::ResponseHeaders
|
67
|
-
@res.should.be.validateable
|
68
|
-
end
|
69
|
-
it 'is true when Etag header present' do
|
70
|
-
@res = MockResponse.new(200, { 'Etag' => '"12345"' }, '')
|
71
|
-
@res.extend Rack::Cache::ResponseHeaders
|
72
|
-
@res.should.be.validateable
|
73
|
-
end
|
74
|
-
it 'is false when no validator is present' do
|
75
|
-
@res = MockResponse.new(200, {}, '')
|
76
|
-
@res.extend Rack::Cache::ResponseHeaders
|
77
|
-
@res.should.not.be.validateable
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
describe '#date' do
|
82
|
-
it 'uses the Date header if present' do
|
83
|
-
@res = MockResponse.new(200, { 'Date' => @one_hour_ago.httpdate }, '')
|
84
|
-
@res.extend Rack::Cache::ResponseHeaders
|
85
|
-
@res.date.should.equal @one_hour_ago
|
86
|
-
end
|
87
|
-
it 'uses the current time when no Date header present' do
|
88
|
-
@res = MockResponse.new(200, {}, '')
|
89
|
-
@res.extend Rack::Cache::ResponseHeaders
|
90
|
-
@res.date.should.be.close Time.now, 1
|
91
|
-
end
|
92
|
-
it 'returns the correct date when the header is modified directly' do
|
93
|
-
@res = MockResponse.new(200, { 'Date' => @one_hour_ago.httpdate }, '')
|
94
|
-
@res.extend Rack::Cache::ResponseHeaders
|
95
|
-
@res.date.should.equal @one_hour_ago
|
96
|
-
@res.headers['Date'] = @now.httpdate
|
97
|
-
@res.date.should.equal @now
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
describe '#expires_at' do
|
102
|
-
it 'returns #date + #max_age when Cache-Control/max-age is present' do
|
103
|
-
@res.headers['Cache-Control'] = 'max-age=500'
|
104
|
-
@res.expires_at.should.equal @res.date + 500
|
105
|
-
end
|
106
|
-
it 'uses the Expires header when present and no Cache-Control/max-age' do
|
107
|
-
@res.headers['Expires'] = @one_hour_ago.httpdate
|
108
|
-
@res.expires_at.should.equal @one_hour_ago
|
109
|
-
end
|
110
|
-
it 'returns nil when no Expires or Cache-Control provided' do
|
111
|
-
@res.expires_at.should.be nil
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
describe '#max_age' do
|
116
|
-
it 'uses s-maxage cache control directive when present' do
|
117
|
-
@res.headers['Cache-Control'] = 's-maxage=600, max-age=0'
|
118
|
-
@res.max_age.should.equal 600
|
119
|
-
end
|
120
|
-
it 'falls back to max-age when no s-maxage directive present' do
|
121
|
-
@res.headers['Cache-Control'] = 'max-age=600'
|
122
|
-
@res.max_age.should.equal 600
|
123
|
-
end
|
124
|
-
it 'falls back to Expires when no max-age or s-maxage directive present' do
|
125
|
-
@res.headers['Cache-Control'] = 'must-revalidate'
|
126
|
-
@res.headers['Expires'] = @one_hour_later.httpdate
|
127
|
-
@res.max_age.should.equal 60 ** 2
|
128
|
-
end
|
129
|
-
it 'gives a #max_age of nil when no freshness information available' do
|
130
|
-
@res.max_age.should.be.nil
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
describe '#freshness_information?' do
|
135
|
-
it 'is true when Expires header is present' do
|
136
|
-
@res.headers['Expires'] = Time.now.httpdate
|
137
|
-
@res.freshness_information?.should.be true
|
138
|
-
end
|
139
|
-
it 'is true when a Cache-Control max-age directive is present' do
|
140
|
-
@res.headers['Cache-Control'] = 'max-age=500'
|
141
|
-
@res.freshness_information?.should.be true
|
142
|
-
end
|
143
|
-
it 'is true when a Cache-Control s-maxage directive is present' do
|
144
|
-
@res.headers['Cache-Control'] = 's-maxage=500'
|
145
|
-
@res.freshness_information?.should.be true
|
146
|
-
end
|
147
|
-
it 'is not true otherwise' do
|
148
|
-
@res.freshness_information?.should.be false
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
describe '#public=' do
|
153
|
-
it 'adds the public Cache-Control directive when set true' do
|
154
|
-
@res.headers['Cache-Control'] = 'max-age=100'
|
155
|
-
@res.public = true
|
156
|
-
@res.headers['Cache-Control'].should.equal 'public, max-age=100'
|
157
|
-
end
|
158
|
-
it 'removes the private Cache-Control directive' do
|
159
|
-
@res.headers['Cache-Control'] = 'private, max-age=100'
|
160
|
-
@res.public = true
|
161
|
-
@res.headers['Cache-Control'].should.equal 'public, max-age=100'
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
describe '#public?' do
|
166
|
-
it 'is true when the public directive is present' do
|
167
|
-
@res.headers['Cache-Control'] = 'public'
|
168
|
-
@res.should.be.public
|
169
|
-
end
|
170
|
-
it 'is false when only the private directive is present' do
|
171
|
-
@res.headers['Cache-Control'] = 'private'
|
172
|
-
@res.should.not.be.public
|
173
|
-
end
|
174
|
-
it 'is false when no Cache-Control header is present' do
|
175
|
-
@res.should.not.be.public
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
describe '#private=' do
|
180
|
-
it 'adds the private Cache-Control directive when set true' do
|
181
|
-
@res.headers['Cache-Control'] = 'max-age=100'
|
182
|
-
@res.private = true
|
183
|
-
@res.headers['Cache-Control'].should.equal 'private, max-age=100'
|
184
|
-
end
|
185
|
-
it 'removes the public Cache-Control directive' do
|
186
|
-
@res.headers['Cache-Control'] = 'public, max-age=100'
|
187
|
-
@res.private = true
|
188
|
-
@res.headers['Cache-Control'].should.equal 'private, max-age=100'
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
describe '#private?' do
|
193
|
-
it 'is true when the private directive is present' do
|
194
|
-
@res.headers['Cache-Control'] = 'private'
|
195
|
-
@res.should.be.private
|
196
|
-
end
|
197
|
-
it 'is false when the private directive is not present' do
|
198
|
-
@res.headers['Cache-Control'] = 'public'
|
199
|
-
@res.should.not.be.private
|
200
|
-
end
|
201
|
-
it 'is false when no Cache-Control header is present' do
|
202
|
-
@res.should.not.be.private
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
describe '#no_cache?' do
|
207
|
-
it 'is true when a Cache-Control no-cache directive is present' do
|
208
|
-
@res.headers['Cache-Control'] = 'no-cache'
|
209
|
-
assert @res.no_cache?
|
210
|
-
end
|
211
|
-
it 'is false otherwise' do
|
212
|
-
assert !@res.no_cache?
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
describe '#must_revalidate?' do
|
217
|
-
it 'is true when a Cache-Control must-revalidate directive is present' do
|
218
|
-
@res.headers['Cache-Control'] = 'private, must-revalidate'
|
219
|
-
assert @res.must_revalidate?
|
220
|
-
end
|
221
|
-
it 'is true when a Cache-Control proxy-revalidate directive is present' do
|
222
|
-
@res.headers['Cache-Control'] = 'public, proxy-revalidate'
|
223
|
-
assert @res.must_revalidate?
|
224
|
-
end
|
225
|
-
it 'is false otherwise' do
|
226
|
-
assert !@res.must_revalidate?
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
describe '#stale?' do
|
231
|
-
it 'is true when TTL cannot be established' do
|
232
|
-
@res.should.be.stale
|
233
|
-
end
|
234
|
-
it 'is false when the TTL is <= 0' do
|
235
|
-
@res.headers['Expires'] = (@res.now + 10).httpdate
|
236
|
-
@res.should.not.be.stale
|
237
|
-
end
|
238
|
-
it 'is true when the TTL is >= 0' do
|
239
|
-
@res.headers['Expires'] = (@res.now - 10).httpdate
|
240
|
-
@res.should.be.stale
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
describe '#ttl' do
|
245
|
-
it 'is nil when no Expires or Cache-Control headers present' do
|
246
|
-
@res.ttl.should.be.nil
|
247
|
-
end
|
248
|
-
it 'uses the Expires header when no max-age is present' do
|
249
|
-
@res.headers['Expires'] = (@res.now + (60**2)).httpdate
|
250
|
-
@res.ttl.should.be.close(60**2, 1)
|
251
|
-
end
|
252
|
-
it 'returns negative values when Expires is in part' do
|
253
|
-
@res.ttl.should.be.nil
|
254
|
-
@res.headers['Expires'] = @one_hour_ago.httpdate
|
255
|
-
@res.ttl.should.be < 0
|
256
|
-
end
|
257
|
-
it 'uses the Cache-Control max-age value when present' do
|
258
|
-
@res.headers['Cache-Control'] = 'max-age=60'
|
259
|
-
@res.ttl.should.be.close(60, 1)
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
describe '#vary' do
|
264
|
-
it 'is nil when no Vary header is present' do
|
265
|
-
@res.vary.should.be.nil
|
266
|
-
end
|
267
|
-
it 'returns the literal value of the Vary header' do
|
268
|
-
@res.headers['Vary'] = 'Foo Bar Baz'
|
269
|
-
@res.vary.should.equal 'Foo Bar Baz'
|
270
|
-
end
|
271
|
-
it 'can be checked for existence using the #vary? method' do
|
272
|
-
@res.should.respond_to :vary?
|
273
|
-
@res.should.not.vary
|
274
|
-
@res.headers['Vary'] = '*'
|
275
|
-
@res.should.vary
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
describe '#vary_header_names' do
|
280
|
-
it 'returns an empty Array when no Vary header is present' do
|
281
|
-
@res.vary_header_names.should.be.empty
|
282
|
-
end
|
283
|
-
it 'parses a single header name value' do
|
284
|
-
@res.headers['Vary'] = 'Accept-Language'
|
285
|
-
@res.vary_header_names.should.equal ['Accept-Language']
|
286
|
-
end
|
287
|
-
it 'parses multiple header name values separated by spaces' do
|
288
|
-
@res.headers['Vary'] = 'Accept-Language User-Agent X-Foo'
|
289
|
-
@res.vary_header_names.should.equal \
|
290
|
-
['Accept-Language', 'User-Agent', 'X-Foo']
|
291
|
-
end
|
292
|
-
it 'parses multiple header name values separated by commas' do
|
293
|
-
@res.headers['Vary'] = 'Accept-Language,User-Agent, X-Foo'
|
294
|
-
@res.vary_header_names.should.equal \
|
295
|
-
['Accept-Language', 'User-Agent', 'X-Foo']
|
296
|
-
end
|
297
|
-
end
|
298
|
-
end
|