dalli 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dalli might be problematic. Click here for more details.
- data/History.md +7 -0
- data/README.md +3 -0
- data/Rakefile +2 -0
- data/lib/active_support/cache/dalli_store.rb +8 -4
- data/lib/dalli.rb +1 -1
- data/lib/dalli/client.rb +21 -5
- data/lib/dalli/server.rb +13 -4
- data/lib/dalli/version.rb +1 -1
- data/lib/rack/session/dalli.rb +30 -10
- data/test/memcached_mock.rb +0 -1
- data/test/test_active_support.rb +0 -1
- data/test/test_dalli.rb +21 -5
- data/test/test_failover.rb +1 -1
- data/test/test_rack_session.rb +314 -0
- data/test/test_sasl.rb +23 -0
- data/test/test_session_store.rb +2 -2
- metadata +26 -9
data/History.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
2.2.0
|
5
|
+
=======
|
6
|
+
|
7
|
+
- Add Rack session with\_lock helper, for Rails 4.0 support [#264]
|
8
|
+
- Accept connection string in the form of a URL (e.g., memcached://user:pass@hostname:port) [glenngillen]
|
9
|
+
- Add touch operation [#228, uzzz]
|
10
|
+
|
4
11
|
2.1.0
|
5
12
|
=======
|
6
13
|
|
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -94,6 +94,7 @@ module ActiveSupport
|
|
94
94
|
options ||= {}
|
95
95
|
name = expanded_key name
|
96
96
|
|
97
|
+
log(:exist, name, options)
|
97
98
|
!read_entry(name, options).nil?
|
98
99
|
end
|
99
100
|
|
@@ -101,18 +102,19 @@ module ActiveSupport
|
|
101
102
|
options ||= {}
|
102
103
|
name = expanded_key name
|
103
104
|
|
105
|
+
log(:delete, name, options)
|
104
106
|
delete_entry(name, options)
|
105
107
|
end
|
106
108
|
|
107
109
|
# Reads multiple keys from the cache using a single call to the
|
108
110
|
# servers for all keys. Keys must be Strings.
|
109
111
|
def read_multi(*names)
|
110
|
-
|
112
|
+
names.extract_options!
|
111
113
|
names = names.flatten
|
112
114
|
mapping = names.inject({}) { |memo, name| memo[escape(expanded_key(name))] = name; memo }
|
113
115
|
instrument(:read_multi, names) do
|
114
116
|
results = @data.get_multi(mapping.keys)
|
115
|
-
results.inject({}) do |memo, (inner,
|
117
|
+
results.inject({}) do |memo, (inner, _)|
|
116
118
|
entry = results[inner]
|
117
119
|
# NB Backwards data compatibility, to be removed at some point
|
118
120
|
memo[mapping[inner]] = (entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry)
|
@@ -162,7 +164,9 @@ module ActiveSupport
|
|
162
164
|
# Clear the entire cache on all memcached servers. This method should
|
163
165
|
# be used with care when using a shared cache.
|
164
166
|
def clear(options=nil)
|
165
|
-
|
167
|
+
instrument(:clear, 'flushing all keys') do
|
168
|
+
@data.flush_all
|
169
|
+
end
|
166
170
|
rescue Dalli::DalliError => e
|
167
171
|
logger.error("DalliError: #{e.message}") if logger
|
168
172
|
raise if @raise_errors
|
@@ -234,7 +238,7 @@ module ActiveSupport
|
|
234
238
|
|
235
239
|
def escape(key)
|
236
240
|
key = key.to_s.dup
|
237
|
-
key = key.force_encoding("BINARY") if key.
|
241
|
+
key = key.force_encoding("BINARY") if key.respond_to?(:encode)
|
238
242
|
key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
239
243
|
key
|
240
244
|
end
|
data/lib/dalli.rb
CHANGED
data/lib/dalli/client.rb
CHANGED
@@ -12,9 +12,8 @@ module Dalli
|
|
12
12
|
# :threadsafe => true, :failover => true, :expires_in => 300)
|
13
13
|
#
|
14
14
|
# servers is an Array of "host:port:weight" where weight allows you to distribute cache unevenly.
|
15
|
-
# Both weight and port are optional. If you pass in nil, Dalli will
|
16
|
-
#
|
17
|
-
# in managed environments like Heroku.
|
15
|
+
# Both weight and port are optional. If you pass in nil, Dalli will use the <tt>MEMCACHE_SERVERS</tt>
|
16
|
+
# environment variable or default to 'localhost:11211' if it is not present.
|
18
17
|
#
|
19
18
|
# Options:
|
20
19
|
# - :namespace - prepend each key with this value to provide simple namespacing.
|
@@ -25,7 +24,7 @@ module Dalli
|
|
25
24
|
# sending them to memcached.
|
26
25
|
#
|
27
26
|
def initialize(servers=nil, options={})
|
28
|
-
@servers =
|
27
|
+
@servers = servers || env_servers || '127.0.0.1:11211'
|
29
28
|
@options = normalize_options(options)
|
30
29
|
@ring = nil
|
31
30
|
end
|
@@ -198,6 +197,16 @@ module Dalli
|
|
198
197
|
perform(:decr, key, amt.to_i, ttl, default)
|
199
198
|
end
|
200
199
|
|
200
|
+
##
|
201
|
+
# Touch updates expiration time for a given key.
|
202
|
+
#
|
203
|
+
# Returns true if key exists, otherwise nil.
|
204
|
+
def touch(key, ttl=nil)
|
205
|
+
ttl ||= @options[:expires_in].to_i
|
206
|
+
resp = perform(:touch, key, ttl)
|
207
|
+
resp.nil? ? nil : true
|
208
|
+
end
|
209
|
+
|
201
210
|
##
|
202
211
|
# Collect the stats for each server.
|
203
212
|
# Returns a hash like { 'hostname:port' => { 'stat1' => 'value1', ... }, 'hostname2:port' => { ... } }
|
@@ -233,7 +242,14 @@ module Dalli
|
|
233
242
|
def ring
|
234
243
|
@ring ||= Dalli::Ring.new(
|
235
244
|
Array(@servers).map do |s|
|
236
|
-
|
245
|
+
server_options = {}
|
246
|
+
if s =~ %r{\Amemcached://}
|
247
|
+
uri = URI.parse(s)
|
248
|
+
server_options[:username] = uri.user
|
249
|
+
server_options[:password] = uri.password
|
250
|
+
s = "#{uri.host}:#{uri.port}"
|
251
|
+
end
|
252
|
+
Dalli::Server.new(s, @options.merge(server_options))
|
237
253
|
end, @options
|
238
254
|
)
|
239
255
|
end
|
data/lib/dalli/server.rb
CHANGED
@@ -38,6 +38,7 @@ module Dalli
|
|
38
38
|
@sock = nil
|
39
39
|
@msg = nil
|
40
40
|
@pid = nil
|
41
|
+
@inprogress = nil
|
41
42
|
end
|
42
43
|
|
43
44
|
# Chokepoint method for instrumentation
|
@@ -261,6 +262,12 @@ module Dalli
|
|
261
262
|
generic_response
|
262
263
|
end
|
263
264
|
|
265
|
+
def touch(key, ttl)
|
266
|
+
req = [REQUEST, OPCODES[:touch], key.bytesize, 4, 0, 0, key.bytesize + 4, 0, 0, ttl, key].pack(FORMAT[:touch])
|
267
|
+
write(req)
|
268
|
+
generic_response
|
269
|
+
end
|
270
|
+
|
264
271
|
COMPRESSION_MIN_SIZE = 1024
|
265
272
|
|
266
273
|
# http://www.hjp.at/zettel/m/memcached_flags.rxml
|
@@ -310,7 +317,7 @@ module Dalli
|
|
310
317
|
def cas_response
|
311
318
|
header = read(24)
|
312
319
|
raise Dalli::NetworkError, 'No response' if !header
|
313
|
-
(extras,
|
320
|
+
(extras, _, status, count, _, cas) = header.unpack(CAS_HEADER)
|
314
321
|
data = read(count) if count > 0
|
315
322
|
if status == 1
|
316
323
|
nil
|
@@ -331,7 +338,7 @@ module Dalli
|
|
331
338
|
def generic_response(unpack=false)
|
332
339
|
header = read(24)
|
333
340
|
raise Dalli::NetworkError, 'No response' if !header
|
334
|
-
(extras,
|
341
|
+
(extras, _, status, count) = header.unpack(NORMAL_HEADER)
|
335
342
|
data = read(count) if count > 0
|
336
343
|
if status == 1
|
337
344
|
nil
|
@@ -353,7 +360,7 @@ module Dalli
|
|
353
360
|
loop do
|
354
361
|
header = read(24)
|
355
362
|
raise Dalli::NetworkError, 'No response' if !header
|
356
|
-
(key_length,
|
363
|
+
(key_length, _, body_length) = header.unpack(KV_HEADER)
|
357
364
|
return hash if key_length == 0
|
358
365
|
key = read(key_length)
|
359
366
|
value = read(body_length - key_length) if body_length - key_length > 0
|
@@ -366,7 +373,7 @@ module Dalli
|
|
366
373
|
loop do
|
367
374
|
header = read(24)
|
368
375
|
raise Dalli::NetworkError, 'No response' if !header
|
369
|
-
(key_length,
|
376
|
+
(key_length, _, body_length) = header.unpack(KV_HEADER)
|
370
377
|
return hash if key_length == 0
|
371
378
|
flags = read(4).unpack('N')[0]
|
372
379
|
key = read(key_length)
|
@@ -462,6 +469,7 @@ module Dalli
|
|
462
469
|
:auth_negotiation => 0x20,
|
463
470
|
:auth_request => 0x21,
|
464
471
|
:auth_continue => 0x22,
|
472
|
+
:touch => 0x1C,
|
465
473
|
}
|
466
474
|
|
467
475
|
HEADER = "CCnCCnNNQ"
|
@@ -482,6 +490,7 @@ module Dalli
|
|
482
490
|
:prepend => 'a*a*',
|
483
491
|
:auth_request => 'a*a*',
|
484
492
|
:auth_continue => 'a*a*',
|
493
|
+
:touch => 'Na*',
|
485
494
|
}
|
486
495
|
FORMAT = OP_FORMAT.inject({}) { |memo, (k, v)| memo[k] = HEADER + v; memo }
|
487
496
|
|
data/lib/dalli/version.rb
CHANGED
data/lib/rack/session/dalli.rb
CHANGED
@@ -4,7 +4,7 @@ require 'dalli'
|
|
4
4
|
module Rack
|
5
5
|
module Session
|
6
6
|
class Dalli < Abstract::ID
|
7
|
-
attr_reader :pool
|
7
|
+
attr_reader :pool, :mutex
|
8
8
|
|
9
9
|
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
|
10
10
|
:namespace => 'rack:session',
|
@@ -12,6 +12,7 @@ module Rack
|
|
12
12
|
|
13
13
|
def initialize(app, options={})
|
14
14
|
super
|
15
|
+
@mutex = Mutex.new
|
15
16
|
mserv = @default_options[:memcache_server]
|
16
17
|
mopts = @default_options.reject{|k,v| !DEFAULT_OPTIONS.include? k }
|
17
18
|
@pool = options[:cache] || ::Dalli::Client.new(mserv, mopts)
|
@@ -25,26 +26,45 @@ module Rack
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def get_session(env, sid)
|
28
|
-
|
29
|
-
sid
|
30
|
-
|
31
|
-
|
29
|
+
with_lock(env, [nil, {}]) do
|
30
|
+
unless sid and session = @pool.get(sid)
|
31
|
+
sid, session = generate_sid, {}
|
32
|
+
unless @pool.add(sid, session)
|
33
|
+
raise "Session collision on '#{sid.inspect}'"
|
34
|
+
end
|
32
35
|
end
|
36
|
+
[sid, session]
|
33
37
|
end
|
34
|
-
[sid, session]
|
35
38
|
end
|
36
39
|
|
37
40
|
def set_session(env, session_id, new_session, options)
|
38
41
|
expiry = options[:expire_after]
|
39
42
|
expiry = expiry.nil? ? 0 : expiry + 1
|
40
43
|
|
41
|
-
|
42
|
-
|
44
|
+
with_lock(env, false) do
|
45
|
+
@pool.set session_id, new_session, expiry
|
46
|
+
session_id
|
47
|
+
end
|
43
48
|
end
|
44
49
|
|
45
50
|
def destroy_session(env, session_id, options)
|
46
|
-
|
47
|
-
|
51
|
+
with_lock(env) do
|
52
|
+
@pool.delete(session_id)
|
53
|
+
generate_sid unless options[:drop]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def with_lock(env, default=nil)
|
58
|
+
@mutex.lock if env['rack.multithread']
|
59
|
+
yield
|
60
|
+
rescue Dalli::DalliError, Errno::ECONNREFUSED
|
61
|
+
if $VERBOSE
|
62
|
+
warn "#{self} is unable to find memcached server."
|
63
|
+
warn $!.inspect
|
64
|
+
end
|
65
|
+
default
|
66
|
+
ensure
|
67
|
+
@mutex.unlock if @mutex.locked?
|
48
68
|
end
|
49
69
|
|
50
70
|
end
|
data/test/memcached_mock.rb
CHANGED
data/test/test_active_support.rb
CHANGED
data/test/test_dalli.rb
CHANGED
@@ -14,12 +14,12 @@ describe 'Dalli' do
|
|
14
14
|
# Rails.logger.expects :warn
|
15
15
|
assert dc.instance_variable_get(:@options)[:compress]
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
should 'raises error with invalid expires_in' do
|
19
19
|
bad_data = [{:bad => 'expires in data'}, Hash, [1,2,3]]
|
20
20
|
bad_data.each do |bad|
|
21
21
|
assert_raises ArgumentError do
|
22
|
-
|
22
|
+
Dalli::Client.new('foo', {:expires_in => bad})
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -282,7 +282,7 @@ describe 'Dalli' do
|
|
282
282
|
|
283
283
|
should 'support the append and prepend operations' do
|
284
284
|
memcached do |dc|
|
285
|
-
|
285
|
+
dc.flush
|
286
286
|
assert_equal true, dc.set('456', 'xyz', 0, :raw => true)
|
287
287
|
assert_equal true, dc.prepend('456', '0')
|
288
288
|
assert_equal true, dc.append('456', '9')
|
@@ -294,6 +294,22 @@ describe 'Dalli' do
|
|
294
294
|
end
|
295
295
|
end
|
296
296
|
|
297
|
+
should 'support touch operation' do
|
298
|
+
memcached do |dc|
|
299
|
+
begin
|
300
|
+
dc.flush
|
301
|
+
dc.set 'key', 'value'
|
302
|
+
assert_equal true, dc.touch('key', 10)
|
303
|
+
assert_equal true, dc.touch('key')
|
304
|
+
assert_equal 'value', dc.get('key')
|
305
|
+
assert_nil dc.touch('notexist')
|
306
|
+
rescue Dalli::DalliError => e
|
307
|
+
# This will happen when memcached is in lesser version than 1.4.8
|
308
|
+
assert_equal 'Response error 129: Unknown command', e.message
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
297
313
|
should 'allow TCP connections to be configured for keepalive' do
|
298
314
|
memcached(19122, '', :keepalive => true) do |dc|
|
299
315
|
dc.set(:a, 1)
|
@@ -394,7 +410,7 @@ describe 'Dalli' do
|
|
394
410
|
assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
|
395
411
|
inc = cache.incr('cat', 10)
|
396
412
|
assert_equal 0, inc % 5
|
397
|
-
|
413
|
+
cache.decr('cat', 5)
|
398
414
|
assert_equal 11, cache.get('b')
|
399
415
|
end
|
400
416
|
end
|
@@ -415,7 +431,7 @@ describe 'Dalli' do
|
|
415
431
|
assert_equal 2, dc2.get('namespaced')
|
416
432
|
end
|
417
433
|
end
|
418
|
-
|
434
|
+
|
419
435
|
should 'truncate cache keys that are too long' do
|
420
436
|
memcached do
|
421
437
|
@dalli = Dalli::Client.new('localhost:19122', :namespace => 'some:namspace')
|
data/test/test_failover.rb
CHANGED
@@ -0,0 +1,314 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
require 'rack/session/dalli'
|
4
|
+
require 'rack/lint'
|
5
|
+
require 'rack/mock'
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
describe Rack::Session::Dalli do
|
9
|
+
session_key = Rack::Session::Dalli::DEFAULT_OPTIONS[:key]
|
10
|
+
session_match = /#{session_key}=([0-9a-fA-F]+);/
|
11
|
+
incrementor = lambda do |env|
|
12
|
+
env["rack.session"]["counter"] ||= 0
|
13
|
+
env["rack.session"]["counter"] += 1
|
14
|
+
Rack::Response.new(env["rack.session"].inspect).to_a
|
15
|
+
end
|
16
|
+
drop_session = Rack::Lint.new(proc do |env|
|
17
|
+
env['rack.session.options'][:drop] = true
|
18
|
+
incrementor.call(env)
|
19
|
+
end)
|
20
|
+
renew_session = Rack::Lint.new(proc do |env|
|
21
|
+
env['rack.session.options'][:renew] = true
|
22
|
+
incrementor.call(env)
|
23
|
+
end)
|
24
|
+
defer_session = Rack::Lint.new(proc do |env|
|
25
|
+
env['rack.session.options'][:defer] = true
|
26
|
+
incrementor.call(env)
|
27
|
+
end)
|
28
|
+
skip_session = Rack::Lint.new(proc do |env|
|
29
|
+
env['rack.session.options'][:skip] = true
|
30
|
+
incrementor.call(env)
|
31
|
+
end)
|
32
|
+
incrementor = Rack::Lint.new(incrementor)
|
33
|
+
|
34
|
+
# test memcache connection
|
35
|
+
Rack::Session::Dalli.new(incrementor)
|
36
|
+
|
37
|
+
it "faults on no connection" do
|
38
|
+
assert_raises Dalli::RingError do
|
39
|
+
rsd = Rack::Session::Dalli.new(incrementor, :memcache_server => 'nosuchserver')
|
40
|
+
rsd.pool.set('ping', '')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "connects to existing server" do
|
45
|
+
assert_silent do
|
46
|
+
rsd = Rack::Session::Dalli.new(incrementor, :namespace => 'test:rack:session')
|
47
|
+
rsd.pool.set('ping', '')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "passes options to MemCache" do
|
52
|
+
rsd = Rack::Session::Dalli.new(incrementor, :namespace => 'test:rack:session')
|
53
|
+
assert_equal('test:rack:session', rsd.pool.instance_eval { @options[:namespace] })
|
54
|
+
end
|
55
|
+
|
56
|
+
it "creates a new cookie" do
|
57
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
58
|
+
res = Rack::MockRequest.new(rsd).get("/")
|
59
|
+
assert res["Set-Cookie"].include?("#{session_key}=")
|
60
|
+
assert_equal '{"counter"=>1}', res.body
|
61
|
+
end
|
62
|
+
|
63
|
+
it "determines session from a cookie" do
|
64
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
65
|
+
req = Rack::MockRequest.new(rsd)
|
66
|
+
res = req.get("/")
|
67
|
+
cookie = res["Set-Cookie"]
|
68
|
+
assert_equal '{"counter"=>2}', req.get("/", "HTTP_COOKIE" => cookie).body
|
69
|
+
assert_equal '{"counter"=>3}', req.get("/", "HTTP_COOKIE" => cookie).body
|
70
|
+
end
|
71
|
+
|
72
|
+
it "determines session only from a cookie by default" do
|
73
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
74
|
+
req = Rack::MockRequest.new(rsd)
|
75
|
+
res = req.get("/")
|
76
|
+
sid = res["Set-Cookie"][session_match, 1]
|
77
|
+
assert_equal '{"counter"=>1}', req.get("/?rack.session=#{sid}").body
|
78
|
+
assert_equal '{"counter"=>1}', req.get("/?rack.session=#{sid}").body
|
79
|
+
end
|
80
|
+
|
81
|
+
it "determines session from params" do
|
82
|
+
rsd = Rack::Session::Dalli.new(incrementor, :cookie_only => false)
|
83
|
+
req = Rack::MockRequest.new(rsd)
|
84
|
+
res = req.get("/")
|
85
|
+
sid = res["Set-Cookie"][session_match, 1]
|
86
|
+
assert_equal '{"counter"=>2}', req.get("/?rack.session=#{sid}").body
|
87
|
+
assert_equal '{"counter"=>3}', req.get("/?rack.session=#{sid}").body
|
88
|
+
end
|
89
|
+
|
90
|
+
it "survives nonexistant cookies" do
|
91
|
+
bad_cookie = "rack.session=blarghfasel"
|
92
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
93
|
+
res = Rack::MockRequest.new(rsd).
|
94
|
+
get("/", "HTTP_COOKIE" => bad_cookie)
|
95
|
+
assert_equal '{"counter"=>1}', res.body
|
96
|
+
cookie = res["Set-Cookie"][session_match]
|
97
|
+
refute_match(/#{bad_cookie}/, cookie)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "maintains freshness" do
|
101
|
+
rsd = Rack::Session::Dalli.new(incrementor, :expire_after => 3)
|
102
|
+
res = Rack::MockRequest.new(rsd).get('/')
|
103
|
+
assert res.body.include?('"counter"=>1')
|
104
|
+
cookie = res["Set-Cookie"]
|
105
|
+
res = Rack::MockRequest.new(rsd).get('/', "HTTP_COOKIE" => cookie)
|
106
|
+
assert_equal cookie, res["Set-Cookie"]
|
107
|
+
assert res.body.include?('"counter"=>2')
|
108
|
+
puts 'Sleeping to expire session' if $DEBUG
|
109
|
+
sleep 4
|
110
|
+
res = Rack::MockRequest.new(rsd).get('/', "HTTP_COOKIE" => cookie)
|
111
|
+
refute_equal cookie, res["Set-Cookie"]
|
112
|
+
assert res.body.include?('"counter"=>1')
|
113
|
+
end
|
114
|
+
|
115
|
+
it "does not send the same session id if it did not change" do
|
116
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
117
|
+
req = Rack::MockRequest.new(rsd)
|
118
|
+
|
119
|
+
res0 = req.get("/")
|
120
|
+
cookie = res0["Set-Cookie"][session_match]
|
121
|
+
assert_equal '{"counter"=>1}', res0.body
|
122
|
+
|
123
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
124
|
+
assert_nil res1["Set-Cookie"]
|
125
|
+
assert_equal '{"counter"=>2}', res1.body
|
126
|
+
|
127
|
+
res2 = req.get("/", "HTTP_COOKIE" => cookie)
|
128
|
+
assert_nil res2["Set-Cookie"]
|
129
|
+
assert_equal '{"counter"=>3}', res2.body
|
130
|
+
end
|
131
|
+
|
132
|
+
it "deletes cookies with :drop option" do
|
133
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
134
|
+
req = Rack::MockRequest.new(rsd)
|
135
|
+
drop = Rack::Utils::Context.new(rsd, drop_session)
|
136
|
+
dreq = Rack::MockRequest.new(drop)
|
137
|
+
|
138
|
+
res1 = req.get("/")
|
139
|
+
session = (cookie = res1["Set-Cookie"])[session_match]
|
140
|
+
assert_equal '{"counter"=>1}', res1.body
|
141
|
+
|
142
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
|
143
|
+
assert_nil res2["Set-Cookie"]
|
144
|
+
assert_equal '{"counter"=>2}', res2.body
|
145
|
+
|
146
|
+
res3 = req.get("/", "HTTP_COOKIE" => cookie)
|
147
|
+
refute_equal session, res3["Set-Cookie"][session_match]
|
148
|
+
assert_equal '{"counter"=>1}', res3.body
|
149
|
+
end
|
150
|
+
|
151
|
+
it "provides new session id with :renew option" do
|
152
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
153
|
+
req = Rack::MockRequest.new(rsd)
|
154
|
+
renew = Rack::Utils::Context.new(rsd, renew_session)
|
155
|
+
rreq = Rack::MockRequest.new(renew)
|
156
|
+
|
157
|
+
res1 = req.get("/")
|
158
|
+
session = (cookie = res1["Set-Cookie"])[session_match]
|
159
|
+
assert_equal '{"counter"=>1}', res1.body
|
160
|
+
|
161
|
+
res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
|
162
|
+
new_cookie = res2["Set-Cookie"]
|
163
|
+
new_session = new_cookie[session_match]
|
164
|
+
refute_equal session, new_session
|
165
|
+
assert_equal '{"counter"=>2}', res2.body
|
166
|
+
|
167
|
+
res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
|
168
|
+
assert_equal '{"counter"=>3}', res3.body
|
169
|
+
|
170
|
+
# Old cookie was deleted
|
171
|
+
res4 = req.get("/", "HTTP_COOKIE" => cookie)
|
172
|
+
assert_equal '{"counter"=>1}', res4.body
|
173
|
+
end
|
174
|
+
|
175
|
+
it "omits cookie with :defer option but still updates the state" do
|
176
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
177
|
+
count = Rack::Utils::Context.new(rsd, incrementor)
|
178
|
+
defer = Rack::Utils::Context.new(rsd, defer_session)
|
179
|
+
dreq = Rack::MockRequest.new(defer)
|
180
|
+
creq = Rack::MockRequest.new(count)
|
181
|
+
|
182
|
+
res0 = dreq.get("/")
|
183
|
+
assert_nil res0["Set-Cookie"]
|
184
|
+
assert_equal '{"counter"=>1}', res0.body
|
185
|
+
|
186
|
+
res0 = creq.get("/")
|
187
|
+
res1 = dreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
|
188
|
+
assert_equal '{"counter"=>2}', res1.body
|
189
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
|
190
|
+
assert_equal '{"counter"=>3}', res2.body
|
191
|
+
end
|
192
|
+
|
193
|
+
it "omits cookie and state update with :skip option" do
|
194
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
195
|
+
count = Rack::Utils::Context.new(rsd, incrementor)
|
196
|
+
skip = Rack::Utils::Context.new(rsd, skip_session)
|
197
|
+
sreq = Rack::MockRequest.new(skip)
|
198
|
+
creq = Rack::MockRequest.new(count)
|
199
|
+
|
200
|
+
res0 = sreq.get("/")
|
201
|
+
assert_nil res0["Set-Cookie"]
|
202
|
+
assert_equal '{"counter"=>1}', res0.body
|
203
|
+
|
204
|
+
res0 = creq.get("/")
|
205
|
+
res1 = sreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
|
206
|
+
assert_equal '{"counter"=>2}', res1.body
|
207
|
+
res2 = sreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
|
208
|
+
assert_equal '{"counter"=>2}', res2.body
|
209
|
+
end
|
210
|
+
|
211
|
+
it "updates deep hashes correctly" do
|
212
|
+
hash_check = proc do |env|
|
213
|
+
session = env['rack.session']
|
214
|
+
unless session.include? 'test'
|
215
|
+
session.update :a => :b, :c => { :d => :e },
|
216
|
+
:f => { :g => { :h => :i} }, 'test' => true
|
217
|
+
else
|
218
|
+
session[:f][:g][:h] = :j
|
219
|
+
end
|
220
|
+
[200, {}, [session.inspect]]
|
221
|
+
end
|
222
|
+
rsd = Rack::Session::Dalli.new(hash_check)
|
223
|
+
req = Rack::MockRequest.new(rsd)
|
224
|
+
|
225
|
+
res0 = req.get("/")
|
226
|
+
session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
|
227
|
+
ses0 = rsd.pool.get(session_id, true)
|
228
|
+
|
229
|
+
req.get("/", "HTTP_COOKIE" => cookie)
|
230
|
+
ses1 = rsd.pool.get(session_id, true)
|
231
|
+
|
232
|
+
refute_equal ses0, ses1
|
233
|
+
end
|
234
|
+
|
235
|
+
# anyone know how to do this better?
|
236
|
+
it "cleanly merges sessions when multithreaded" do
|
237
|
+
unless $DEBUG
|
238
|
+
assert_equal 1, 1 # fake assertion to appease the mighty bacon
|
239
|
+
next
|
240
|
+
end
|
241
|
+
warn 'Running multithread test for Session::Dalli'
|
242
|
+
rsd = Rack::Session::Dalli.new(incrementor)
|
243
|
+
req = Rack::MockRequest.new(rsd)
|
244
|
+
|
245
|
+
res = req.get('/')
|
246
|
+
assert_equal '{"counter"=>1}', res.body
|
247
|
+
cookie = res["Set-Cookie"]
|
248
|
+
session_id = cookie[session_match, 1]
|
249
|
+
|
250
|
+
delta_incrementor = lambda do |env|
|
251
|
+
# emulate disconjoinment of threading
|
252
|
+
env['rack.session'] = env['rack.session'].dup
|
253
|
+
Thread.stop
|
254
|
+
env['rack.session'][(Time.now.usec*rand).to_i] = true
|
255
|
+
incrementor.call(env)
|
256
|
+
end
|
257
|
+
tses = Rack::Utils::Context.new rsd, delta_incrementor
|
258
|
+
treq = Rack::MockRequest.new(tses)
|
259
|
+
tnum = rand(7).to_i+5
|
260
|
+
r = Array.new(tnum) do
|
261
|
+
Thread.new(treq) do |run|
|
262
|
+
run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
|
263
|
+
end
|
264
|
+
end.reverse.map{|t| t.run.join.value }
|
265
|
+
r.each do |request|
|
266
|
+
assert_equal cookie, request['Set-Cookie']
|
267
|
+
assert request.body.include?('"counter"=>2')
|
268
|
+
end
|
269
|
+
|
270
|
+
session = rsd.pool.get(session_id)
|
271
|
+
assert_equal tnum+1, session.size # counter
|
272
|
+
assert_equal 2, session['counter'] # meeeh
|
273
|
+
|
274
|
+
tnum = rand(7).to_i+5
|
275
|
+
r = Array.new(tnum) do |i|
|
276
|
+
app = Rack::Utils::Context.new rsd, time_delta
|
277
|
+
req = Rack::MockRequest.new app
|
278
|
+
Thread.new(req) do |run|
|
279
|
+
run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
|
280
|
+
end
|
281
|
+
end.reverse.map{|t| t.run.join.value }
|
282
|
+
r.each do |request|
|
283
|
+
assert_equal cookie, request['Set-Cookie']
|
284
|
+
assert request.body.include?('"counter"=>3')
|
285
|
+
end
|
286
|
+
|
287
|
+
session = rsd.pool.get(session_id)
|
288
|
+
assert_equal tnum+1, session.size
|
289
|
+
assert_equal 3, session['counter']
|
290
|
+
|
291
|
+
drop_counter = proc do |env|
|
292
|
+
env['rack.session'].delete 'counter'
|
293
|
+
env['rack.session']['foo'] = 'bar'
|
294
|
+
[200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
|
295
|
+
end
|
296
|
+
tses = Rack::Utils::Context.new rsd, drop_counter
|
297
|
+
treq = Rack::MockRequest.new(tses)
|
298
|
+
tnum = rand(7).to_i+5
|
299
|
+
r = Array.new(tnum) do
|
300
|
+
Thread.new(treq) do |run|
|
301
|
+
run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
|
302
|
+
end
|
303
|
+
end.reverse.map{|t| t.run.join.value }
|
304
|
+
r.each do |request|
|
305
|
+
assert_equal cookie, request['Set-Cookie']
|
306
|
+
assert request.body.include?('"foo"=>"bar"')
|
307
|
+
end
|
308
|
+
|
309
|
+
session = rsd.pool.get(session_id)
|
310
|
+
assert_equal r.size+1, session.size
|
311
|
+
assert_nil session['counter']
|
312
|
+
assert_equal 'bar', session['foo']
|
313
|
+
end
|
314
|
+
end
|
data/test/test_sasl.rb
CHANGED
@@ -3,6 +3,13 @@ require 'helper'
|
|
3
3
|
describe 'Sasl' do
|
4
4
|
|
5
5
|
context 'a server requiring authentication' do
|
6
|
+
before do
|
7
|
+
@server = mock()
|
8
|
+
@server.stubs(:request).returns(true)
|
9
|
+
@server.stubs(:weight).returns(1)
|
10
|
+
@server.stubs(:hostname).returns("localhost")
|
11
|
+
@server.stubs(:port).returns("19124")
|
12
|
+
end
|
6
13
|
|
7
14
|
context 'without authentication credentials' do
|
8
15
|
before do
|
@@ -79,5 +86,21 @@ describe 'Sasl' do
|
|
79
86
|
end
|
80
87
|
end
|
81
88
|
|
89
|
+
should 'pass SASL as URI' do
|
90
|
+
Dalli::Server.expects(:new).with("localhost:19124",
|
91
|
+
:username => "testuser", :password => "testtest").returns(@server)
|
92
|
+
dc = Dalli::Client.new('memcached://testuser:testtest@localhost:19124')
|
93
|
+
dc.flush_all
|
94
|
+
end
|
95
|
+
|
96
|
+
should 'pass SASL as ring of URIs' do
|
97
|
+
Dalli::Server.expects(:new).with("localhost:19124",
|
98
|
+
:username => "testuser", :password => "testtest").returns(@server)
|
99
|
+
Dalli::Server.expects(:new).with("otherhost:19125",
|
100
|
+
:username => "testuser2", :password => "testtest2").returns(@server)
|
101
|
+
dc = Dalli::Client.new(['memcached://testuser:testtest@localhost:19124',
|
102
|
+
'memcached://testuser2:testtest2@otherhost:19125'])
|
103
|
+
dc.flush_all
|
104
|
+
end
|
82
105
|
end
|
83
106
|
end
|
data/test/test_session_store.rb
CHANGED
@@ -183,7 +183,7 @@ class TestSessionStore < ActionController::IntegrationTest
|
|
183
183
|
def test_expire_after
|
184
184
|
with_test_route_set(:expire_after => 1) do
|
185
185
|
get '/set_session_value'
|
186
|
-
assert_match
|
186
|
+
assert_match(/expires/, @response.headers['Set-Cookie'])
|
187
187
|
|
188
188
|
sleep(1)
|
189
189
|
|
@@ -195,7 +195,7 @@ class TestSessionStore < ActionController::IntegrationTest
|
|
195
195
|
def test_expires_in
|
196
196
|
with_test_route_set(:expires_in => 1) do
|
197
197
|
get '/set_session_value'
|
198
|
-
assert_no_match
|
198
|
+
assert_no_match(/expires/, @response.headers['Set-Cookie'])
|
199
199
|
|
200
200
|
sleep(1)
|
201
201
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dalli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mini_shoulda
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: mocha
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rails
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ~>
|
@@ -43,7 +53,12 @@ dependencies:
|
|
43
53
|
version: '3'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3'
|
47
62
|
description: High performance memcached client for Ruby
|
48
63
|
email: mperham@gmail.com
|
49
64
|
executables: []
|
@@ -77,6 +92,7 @@ files:
|
|
77
92
|
- test/test_encoding.rb
|
78
93
|
- test/test_failover.rb
|
79
94
|
- test/test_network.rb
|
95
|
+
- test/test_rack_session.rb
|
80
96
|
- test/test_ring.rb
|
81
97
|
- test/test_sasl.rb
|
82
98
|
- test/test_session_store.rb
|
@@ -101,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
117
|
version: '0'
|
102
118
|
requirements: []
|
103
119
|
rubyforge_project:
|
104
|
-
rubygems_version: 1.8.
|
120
|
+
rubygems_version: 1.8.24
|
105
121
|
signing_key:
|
106
122
|
specification_version: 3
|
107
123
|
summary: High performance memcached client for Ruby
|
@@ -115,6 +131,7 @@ test_files:
|
|
115
131
|
- test/test_encoding.rb
|
116
132
|
- test/test_failover.rb
|
117
133
|
- test/test_network.rb
|
134
|
+
- test/test_rack_session.rb
|
118
135
|
- test/test_ring.rb
|
119
136
|
- test/test_sasl.rb
|
120
137
|
- test/test_session_store.rb
|