dalli 0.9.2 → 0.9.3
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 +9 -0
- data/README.md +42 -3
- data/Rakefile +5 -0
- data/TODO.md +1 -0
- data/Upgrade.md +44 -0
- data/dalli.gemspec +2 -1
- data/lib/action_dispatch/middleware/session/dalli_store.rb +76 -0
- data/lib/active_support/cache/dalli_store.rb +4 -0
- data/lib/active_support/cache/dalli_store23.rb +172 -0
- data/lib/dalli/client.rb +23 -14
- data/lib/dalli/server.rb +0 -2
- data/lib/dalli/version.rb +1 -1
- data/test/abstract_unit.rb +285 -0
- data/test/{test_benchmark.rb → benchmark_test.rb} +0 -0
- data/test/helper.rb +23 -1
- data/test/memcached_mock.rb +2 -7
- data/test/test_active_support.rb +72 -54
- data/test/test_dalli.rb +5 -1
- data/test/test_session_store.rb +200 -0
- metadata +12 -8
- data/lib/dalli/sasl/base64.rb +0 -14
- data/lib/dalli/sasl/digest_md5.rb +0 -175
data/test/test_dalli.rb
CHANGED
@@ -73,6 +73,7 @@ class TestDalli < Test::Unit::TestCase
|
|
73
73
|
|
74
74
|
should "support multi-get" do
|
75
75
|
memcached do |dc|
|
76
|
+
dc.close
|
76
77
|
dc.flush
|
77
78
|
resp = dc.get_multi(%w(a b c d e f))
|
78
79
|
assert_equal({}, resp)
|
@@ -129,6 +130,7 @@ class TestDalli < Test::Unit::TestCase
|
|
129
130
|
# rollover the 64-bit value, we'll get something undefined.
|
130
131
|
resp = dc.incr('big', 1)
|
131
132
|
assert_not_equal 0x10000000000000000, resp
|
133
|
+
dc.reset
|
132
134
|
end
|
133
135
|
end
|
134
136
|
|
@@ -179,11 +181,13 @@ class TestDalli < Test::Unit::TestCase
|
|
179
181
|
|
180
182
|
resp = dc.stats
|
181
183
|
assert_equal Hash, resp.class
|
184
|
+
|
185
|
+
dc.close
|
182
186
|
end
|
183
187
|
end
|
184
188
|
|
185
189
|
should "support multithreaded access" do
|
186
|
-
memcached(
|
190
|
+
memcached(19123) do |cache|
|
187
191
|
cache.flush
|
188
192
|
workers = []
|
189
193
|
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
require 'abstract_unit'
|
4
|
+
require 'action_dispatch/middleware/session/dalli_store'
|
5
|
+
|
6
|
+
class Foo
|
7
|
+
def initialize(bar='baz')
|
8
|
+
@bar = bar
|
9
|
+
end
|
10
|
+
def inspect
|
11
|
+
"#<#{self.class} bar:#{@bar.inspect}>"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestSessionStore < ActionController::IntegrationTest
|
16
|
+
class TestController < ActionController::Base
|
17
|
+
def no_session_access
|
18
|
+
head :ok
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_session_value
|
22
|
+
session[:foo] = "bar"
|
23
|
+
head :ok
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_serialized_session_value
|
27
|
+
session[:foo] = Foo.new
|
28
|
+
head :ok
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_session_value
|
32
|
+
render :text => "foo: #{session[:foo].inspect}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_session_id
|
36
|
+
render :text => "#{request.session_options[:id]}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def call_reset_session
|
40
|
+
session[:bar]
|
41
|
+
reset_session
|
42
|
+
session[:bar] = "baz"
|
43
|
+
head :ok
|
44
|
+
end
|
45
|
+
|
46
|
+
def rescue_action(e) raise end
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
require 'dalli'
|
51
|
+
memcache = Dalli::Client.new('localhost:11211')
|
52
|
+
memcache.set('ping', '')
|
53
|
+
|
54
|
+
def test_setting_and_getting_session_value
|
55
|
+
with_test_route_set do
|
56
|
+
get '/set_session_value'
|
57
|
+
assert_response :success
|
58
|
+
assert cookies['_session_id']
|
59
|
+
|
60
|
+
get '/get_session_value'
|
61
|
+
assert_response :success
|
62
|
+
assert_equal 'foo: "bar"', response.body
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_getting_nil_session_value
|
67
|
+
with_test_route_set do
|
68
|
+
get '/get_session_value'
|
69
|
+
assert_response :success
|
70
|
+
assert_equal 'foo: nil', response.body
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_getting_session_value_after_session_reset
|
75
|
+
with_test_route_set do
|
76
|
+
get '/set_session_value'
|
77
|
+
assert_response :success
|
78
|
+
assert cookies['_session_id']
|
79
|
+
session_cookie = cookies.send(:hash_for)['_session_id']
|
80
|
+
|
81
|
+
get '/call_reset_session'
|
82
|
+
assert_response :success
|
83
|
+
assert_not_equal [], headers['Set-Cookie']
|
84
|
+
|
85
|
+
cookies << session_cookie # replace our new session_id with our old, pre-reset session_id
|
86
|
+
|
87
|
+
get '/get_session_value'
|
88
|
+
assert_response :success
|
89
|
+
assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from memcached"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_getting_from_nonexistent_session
|
94
|
+
with_test_route_set do
|
95
|
+
get '/get_session_value'
|
96
|
+
assert_response :success
|
97
|
+
assert_equal 'foo: nil', response.body
|
98
|
+
assert_nil cookies['_session_id'], "should only create session on write, not read"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_setting_session_value_after_session_reset
|
103
|
+
with_test_route_set do
|
104
|
+
get '/set_session_value'
|
105
|
+
assert_response :success
|
106
|
+
assert cookies['_session_id']
|
107
|
+
session_id = cookies['_session_id']
|
108
|
+
|
109
|
+
get '/call_reset_session'
|
110
|
+
assert_response :success
|
111
|
+
assert_not_equal [], headers['Set-Cookie']
|
112
|
+
|
113
|
+
get '/get_session_value'
|
114
|
+
assert_response :success
|
115
|
+
assert_equal 'foo: nil', response.body
|
116
|
+
|
117
|
+
get '/get_session_id'
|
118
|
+
assert_response :success
|
119
|
+
assert_not_equal session_id, response.body
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_getting_session_id
|
124
|
+
with_test_route_set do
|
125
|
+
get '/set_session_value'
|
126
|
+
assert_response :success
|
127
|
+
assert cookies['_session_id']
|
128
|
+
session_id = cookies['_session_id']
|
129
|
+
|
130
|
+
get '/get_session_id'
|
131
|
+
assert_response :success
|
132
|
+
assert_equal session_id, response.body, "should be able to read session id without accessing the session hash"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_deserializes_unloaded_class
|
137
|
+
with_test_route_set do
|
138
|
+
with_autoload_path "session_autoload_test" do
|
139
|
+
get '/set_serialized_session_value'
|
140
|
+
assert_response :success
|
141
|
+
assert cookies['_session_id']
|
142
|
+
end
|
143
|
+
with_autoload_path "session_autoload_test" do
|
144
|
+
get '/get_session_id'
|
145
|
+
assert_response :success
|
146
|
+
end
|
147
|
+
with_autoload_path "session_autoload_test" do
|
148
|
+
get '/get_session_value'
|
149
|
+
assert_response :success
|
150
|
+
assert_equal 'foo: #<Foo bar:"baz">', response.body, "should auto-load unloaded class"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_doesnt_write_session_cookie_if_session_id_is_already_exists
|
156
|
+
with_test_route_set do
|
157
|
+
get '/set_session_value'
|
158
|
+
assert_response :success
|
159
|
+
assert cookies['_session_id']
|
160
|
+
|
161
|
+
get '/get_session_value'
|
162
|
+
assert_response :success
|
163
|
+
assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_prevents_session_fixation
|
168
|
+
with_test_route_set do
|
169
|
+
get '/get_session_value'
|
170
|
+
assert_response :success
|
171
|
+
assert_equal 'foo: nil', response.body
|
172
|
+
session_id = cookies['_session_id']
|
173
|
+
|
174
|
+
reset!
|
175
|
+
|
176
|
+
get '/set_session_value', :_session_id => session_id
|
177
|
+
assert_response :success
|
178
|
+
assert_not_equal session_id, cookies['_session_id']
|
179
|
+
end
|
180
|
+
end
|
181
|
+
rescue LoadError, RuntimeError
|
182
|
+
$stderr.puts "Skipping TestSessionStore tests. Start memcached and try again."
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
def with_test_route_set
|
187
|
+
with_routing do |set|
|
188
|
+
set.draw do |map|
|
189
|
+
match ':action', :to => ::TestSessionStore::TestController
|
190
|
+
end
|
191
|
+
|
192
|
+
@app = self.class.build_app(set) do |middleware|
|
193
|
+
middleware.use ActionDispatch::Session::DalliStore, :key => '_session_id'
|
194
|
+
middleware.delete "ActionDispatch::ShowExceptions"
|
195
|
+
end
|
196
|
+
|
197
|
+
yield
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
8
|
+
- 3
|
9
|
+
version: 0.9.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mike Perham
|
@@ -55,8 +55,7 @@ dependencies:
|
|
55
55
|
- 3
|
56
56
|
- 0
|
57
57
|
- 0
|
58
|
-
|
59
|
-
version: 3.0.0.rc2
|
58
|
+
version: 3.0.0
|
60
59
|
type: :development
|
61
60
|
version_requirements: *id003
|
62
61
|
- !ruby/object:Gem::Dependency
|
@@ -83,14 +82,14 @@ extensions: []
|
|
83
82
|
extra_rdoc_files: []
|
84
83
|
|
85
84
|
files:
|
85
|
+
- lib/action_dispatch/middleware/session/dalli_store.rb
|
86
86
|
- lib/active_support/cache/dalli_store.rb
|
87
|
+
- lib/active_support/cache/dalli_store23.rb
|
87
88
|
- lib/dalli/client.rb
|
88
89
|
- lib/dalli/options.rb
|
89
90
|
- lib/dalli/ring.rb
|
90
91
|
- lib/dalli/sasl/anonymous.rb
|
91
92
|
- lib/dalli/sasl/base.rb
|
92
|
-
- lib/dalli/sasl/base64.rb
|
93
|
-
- lib/dalli/sasl/digest_md5.rb
|
94
93
|
- lib/dalli/sasl/plain.rb
|
95
94
|
- lib/dalli/server.rb
|
96
95
|
- lib/dalli/version.rb
|
@@ -103,12 +102,15 @@ files:
|
|
103
102
|
- Gemfile
|
104
103
|
- dalli.gemspec
|
105
104
|
- Performance.md
|
105
|
+
- Upgrade.md
|
106
|
+
- test/abstract_unit.rb
|
107
|
+
- test/benchmark_test.rb
|
106
108
|
- test/helper.rb
|
107
109
|
- test/memcached_mock.rb
|
108
110
|
- test/test_active_support.rb
|
109
|
-
- test/test_benchmark.rb
|
110
111
|
- test/test_dalli.rb
|
111
112
|
- test/test_network.rb
|
113
|
+
- test/test_session_store.rb
|
112
114
|
has_rdoc: true
|
113
115
|
homepage: http://github.com/mperham/dalli
|
114
116
|
licenses: []
|
@@ -142,9 +144,11 @@ signing_key:
|
|
142
144
|
specification_version: 3
|
143
145
|
summary: High performance memcached client for Ruby
|
144
146
|
test_files:
|
147
|
+
- test/abstract_unit.rb
|
148
|
+
- test/benchmark_test.rb
|
145
149
|
- test/helper.rb
|
146
150
|
- test/memcached_mock.rb
|
147
151
|
- test/test_active_support.rb
|
148
|
-
- test/test_benchmark.rb
|
149
152
|
- test/test_dalli.rb
|
150
153
|
- test/test_network.rb
|
154
|
+
- test/test_session_store.rb
|
data/lib/dalli/sasl/base64.rb
DELETED
@@ -1,175 +0,0 @@
|
|
1
|
-
require 'digest/md5'
|
2
|
-
|
3
|
-
module SASL
|
4
|
-
##
|
5
|
-
# RFC 2831:
|
6
|
-
# http://tools.ietf.org/html/rfc2831
|
7
|
-
class DigestMD5 < Mechanism
|
8
|
-
attr_writer :cnonce
|
9
|
-
|
10
|
-
def initialize(*a)
|
11
|
-
super
|
12
|
-
@nonce_count = 0
|
13
|
-
end
|
14
|
-
|
15
|
-
def start
|
16
|
-
@state = nil
|
17
|
-
unless defined? @nonce
|
18
|
-
['auth', nil]
|
19
|
-
else
|
20
|
-
# reauthentication
|
21
|
-
receive('challenge', '')
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def receive(message_name, content)
|
26
|
-
if message_name == 'challenge'
|
27
|
-
c = decode_challenge(content)
|
28
|
-
|
29
|
-
unless c['rspauth']
|
30
|
-
response = {}
|
31
|
-
if defined?(@nonce) && response['nonce'].nil?
|
32
|
-
# Could be reauth
|
33
|
-
else
|
34
|
-
# No reauth:
|
35
|
-
@nonce_count = 0
|
36
|
-
end
|
37
|
-
@nonce ||= c['nonce']
|
38
|
-
response['nonce'] = @nonce
|
39
|
-
response['charset'] = 'utf-8'
|
40
|
-
response['username'] = preferences.username
|
41
|
-
response['realm'] = c['realm'] || preferences.realm
|
42
|
-
@cnonce = generate_nonce unless defined? @cnonce
|
43
|
-
response['cnonce'] = @cnonce
|
44
|
-
@nc = next_nc
|
45
|
-
response['nc'] = @nc
|
46
|
-
@qop = c['qop'] || 'auth'
|
47
|
-
response['qop'] = @qop
|
48
|
-
response['digest-uri'] = preferences.digest_uri #"memcached/#{self.hostname}"
|
49
|
-
response['response'] = response_value(response['nonce'], response['nc'], response['cnonce'], response['qop'])
|
50
|
-
['response', encode_response(response)]
|
51
|
-
else
|
52
|
-
rspauth_expected = response_value(@nonce, @nc, @cnonce, @qop, '')
|
53
|
-
p :rspauth_received=>c['rspauth'], :rspauth_expected=>rspauth_expected
|
54
|
-
if c['rspauth'] == rspauth_expected
|
55
|
-
['response', nil]
|
56
|
-
else
|
57
|
-
# Bogus server?
|
58
|
-
@state = :failure
|
59
|
-
['failure', nil]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
else
|
63
|
-
# No challenge? Might be success or failure
|
64
|
-
super
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def decode_challenge(text)
|
71
|
-
challenge = {}
|
72
|
-
|
73
|
-
state = :key
|
74
|
-
key = ''
|
75
|
-
value = ''
|
76
|
-
|
77
|
-
text.scan(/./) do |ch|
|
78
|
-
if state == :key
|
79
|
-
if ch == '='
|
80
|
-
state = :value
|
81
|
-
elsif ch =~ /\S/
|
82
|
-
key += ch
|
83
|
-
end
|
84
|
-
|
85
|
-
elsif state == :value
|
86
|
-
if ch == ','
|
87
|
-
challenge[key] = value
|
88
|
-
key = ''
|
89
|
-
value = ''
|
90
|
-
state = :key
|
91
|
-
elsif ch == '"' and value == ''
|
92
|
-
state = :quote
|
93
|
-
else
|
94
|
-
value += ch
|
95
|
-
end
|
96
|
-
|
97
|
-
elsif state == :quote
|
98
|
-
if ch == '"'
|
99
|
-
state = :value
|
100
|
-
else
|
101
|
-
value += ch
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
challenge[key] = value unless key == ''
|
106
|
-
|
107
|
-
p :decode_challenge => challenge
|
108
|
-
challenge
|
109
|
-
end
|
110
|
-
|
111
|
-
def encode_response(response)
|
112
|
-
p :encode_response => response
|
113
|
-
response.collect do |k,v|
|
114
|
-
if v.include?('"')
|
115
|
-
v.sub!('\\', '\\\\')
|
116
|
-
v.sub!('"', '\\"')
|
117
|
-
"#{k}=\"#{v}\""
|
118
|
-
else
|
119
|
-
"#{k}=#{v}"
|
120
|
-
end
|
121
|
-
end.join(',')
|
122
|
-
end
|
123
|
-
|
124
|
-
def generate_nonce
|
125
|
-
nonce = ''
|
126
|
-
while nonce.length < 16
|
127
|
-
c = rand(128).chr
|
128
|
-
nonce += c if c =~ /^[a-zA-Z0-9]$/
|
129
|
-
end
|
130
|
-
nonce
|
131
|
-
end
|
132
|
-
|
133
|
-
##
|
134
|
-
# Function from RFC2831
|
135
|
-
def h(s); Digest::MD5.digest(s); end
|
136
|
-
##
|
137
|
-
# Function from RFC2831
|
138
|
-
def hh(s); Digest::MD5.hexdigest(s); end
|
139
|
-
|
140
|
-
##
|
141
|
-
# Calculate the value for the response field
|
142
|
-
def response_value(nonce, nc, cnonce, qop, a2_prefix='AUTHENTICATE')
|
143
|
-
p :response_value => {:nonce=>nonce,
|
144
|
-
:cnonce=>cnonce,
|
145
|
-
:qop=>qop,
|
146
|
-
:username=>preferences.username,
|
147
|
-
:realm=>preferences.realm,
|
148
|
-
:password=>preferences.password,
|
149
|
-
:authzid=>preferences.authzid}
|
150
|
-
a1_h = h("#{preferences.username}:#{preferences.realm}:#{preferences.password}")
|
151
|
-
a1 = "#{a1_h}:#{nonce}:#{cnonce}"
|
152
|
-
if preferences.authzid
|
153
|
-
a1 += ":#{preferences.authzid}"
|
154
|
-
end
|
155
|
-
if qop && (qop.downcase == 'auth-int' || qop.downcase == 'auth-conf')
|
156
|
-
a2 = "#{a2_prefix}:#{preferences.digest_uri}:00000000000000000000000000000000"
|
157
|
-
else
|
158
|
-
a2 = "#{a2_prefix}:#{preferences.digest_uri}"
|
159
|
-
end
|
160
|
-
hh("#{hh(a1)}:#{nonce}:#{nc}:#{cnonce}:#{qop}:#{hh(a2)}")
|
161
|
-
end
|
162
|
-
|
163
|
-
def next_nc
|
164
|
-
@nonce_count += 1
|
165
|
-
s = @nonce_count.to_s
|
166
|
-
s = "0#{s}" while s.length < 8
|
167
|
-
s
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
# TODO: need to test
|
172
|
-
#MECHANISMS['DIGEST-MD5'] = SASL::DigestMD5
|
173
|
-
|
174
|
-
end
|
175
|
-
|