redis-session-store 0.6.6 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- Mjc5NmUwYjhlZjRlZDZkMTYxYmFmNDM2Mzg4ZTI1MmVkNzgxNjUxNA==
5
- data.tar.gz: !binary |-
6
- OGU1ZGZhYTU3MmYzZWRlMzAzOTdmMWE4NjdmMDQxMDlkMDExOTE2NQ==
2
+ SHA1:
3
+ metadata.gz: b4d846842edbd5ebc0a653e24f80ca303150c390
4
+ data.tar.gz: be0006a20a902ca97e52e6907b90c9a1dfa22bdb
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OWFmYjQ1MTJlODFjZGUxM2RiZDBhYzRkYTczYzUyZTAxYjQxYmQ1ZjU1YjQ2
10
- YzhjZjhiMjY1YzIzMDNkNjJkNTMzZGY2YWE5N2FhYTI2NzdlZjYwMTQzZjU5
11
- YjU0M2E0ZGNlOTJhNzQ1ZWYwMzAwYWQwMGJiZjE0NDQwOTAyMDU=
12
- data.tar.gz: !binary |-
13
- Y2YwMGRjZWFhMGFhYjg5NGEzNTBkMDljOGI0NGNjYjcxNWRkZWIyYzEyNmJm
14
- ODFjYjAyYjQxNWMxNzVlZTc1NDBhNWMwMmRhNGYzMTA1ZDVhNjBhY2Q3MGM1
15
- OGQ3ZmYxODcwZjY5MmFiMzIwNDNlOWY1YzlmYWY2ZTEzZmE3NWE=
6
+ metadata.gz: fa7412de4681ecc8646093aaa4920ae8b4b5683a814cbf9ef73d04003995aefa2c2f8a64371cc73c496dbe517b49b9b934cb6b9975cc754ba40403db82a3a0ca
7
+ data.tar.gz: d250e8895eed17ad83493c0bcab9c92c05c6ca92e88f41f05ae11f06c7c97e51923d39ea1e6b017554453885f8111166ec025b6be4875b3611813f4a8e410a0b
@@ -1,6 +1,12 @@
1
1
  redis-session-store history
2
2
  ===========================
3
3
 
4
+ ## v0.7.0 (2014-04-22)
5
+
6
+ * Fix issue #38, we now delay writing to redis until a session exists. This is
7
+ a backwards-incompatible change, as it removes the `on_sid_collision` option.
8
+ There is now no checking for sid collisions, however that is very unlikely.
9
+
4
10
  ## v0.6.6 (2014-04-08)
5
11
 
6
12
  * Fix issue #37, use correct constant for `ENV_SESSION_OPTIONS_KEY` if not
data/README.md CHANGED
@@ -48,19 +48,6 @@ My::Application.config.session_store :redis_session_store, {
48
48
  }
49
49
  ```
50
50
 
51
- ### Session ID collision handling
52
-
53
- If you want to handle cases where the generated session ID (sid)
54
- collides with an existing session ID, a custom callable handler may be
55
- provided as `on_sid_collision`:
56
-
57
- ``` ruby
58
- My::Application.config.session_store :redis_session_store, {
59
- # ... other options ...
60
- on_sid_collision: ->(sid) { Rails.logger.warn("SID collision! #{sid}") }
61
- }
62
- ```
63
-
64
51
  ### Redis unavailability handling
65
52
 
66
53
  If you want to handle cases where Redis is unavailable, a custom
@@ -4,7 +4,7 @@ require 'redis'
4
4
  # Redis session storage for Rails, and for Rails only. Derived from
5
5
  # the MemCacheStore code, simply dropping in Redis instead.
6
6
  class RedisSessionStore < ActionDispatch::Session::AbstractStore
7
- VERSION = '0.6.6'
7
+ VERSION = '0.7.0'
8
8
  # Rails 3.1 and beyond defines the constant elsewhere
9
9
  unless defined?(ENV_SESSION_OPTIONS_KEY)
10
10
  ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY
@@ -18,7 +18,6 @@ class RedisSessionStore < ActionDispatch::Session::AbstractStore
18
18
  # * +:db+ - Database number, defaults to 0.
19
19
  # * +:key_prefix+ - Prefix for keys used in Redis, e.g. +myapp:+
20
20
  # * +:expire_after+ - A number in seconds for session timeout
21
- # * +:on_sid_collision:+ - Called with SID string when generated SID collides
22
21
  # * +:on_redis_down:+ - Called with err, env, and SID on Errno::ECONNREFUSED
23
22
  # * +:on_session_load_error:+ - Called with err and SID on Marshal.load fail
24
23
  # * +:serializer:+ - Serializer to use on session data, default is :marshal.
@@ -34,7 +33,6 @@ class RedisSessionStore < ActionDispatch::Session::AbstractStore
34
33
  # host: 'host', # Redis host name, default is localhost
35
34
  # port: 12345 # Redis port, default is 6379
36
35
  # },
37
- # on_sid_collision: ->(sid) { logger.warn("SID collision! #{sid}") },
38
36
  # on_redis_down: ->(*a) { logger.error("Redis down! #{a.inspect}") }
39
37
  # serializer: :hybrid # migrate from Marshal to JSON
40
38
  # }
@@ -47,21 +45,35 @@ class RedisSessionStore < ActionDispatch::Session::AbstractStore
47
45
  @default_options.merge!(namespace: 'rack:session')
48
46
  @default_options.merge!(redis_options)
49
47
  @redis = Redis.new(redis_options)
50
- @on_sid_collision = options[:on_sid_collision]
51
48
  @on_redis_down = options[:on_redis_down]
52
49
  @serializer = determine_serializer(options[:serializer])
53
50
  @on_session_load_error = options[:on_session_load_error]
54
51
  verify_handlers!
55
52
  end
56
53
 
57
- attr_accessor :on_sid_collision, :on_redis_down, :on_session_load_error
54
+ attr_accessor :on_redis_down, :on_session_load_error
58
55
 
59
56
  private
60
57
 
61
58
  attr_reader :redis, :key, :default_options, :serializer
62
59
 
60
+ # overrides method defined in rack to actually verify session existence
61
+ # Prevents needless new sessions from being created in scenario where
62
+ # user HAS session id, but it already expired, or is invalid for some
63
+ # other reason, and session was accessed only for reading.
64
+ def session_exists?(env)
65
+ value = current_session_id(env)
66
+
67
+ value && !value.empty? &&
68
+ redis.exists(prefixed(value)) # new behavior
69
+ rescue Errno::ECONNREFUSED => e
70
+ on_redis_down.call(e, env, value) if on_redis_down
71
+
72
+ true
73
+ end
74
+
63
75
  def verify_handlers!
64
- %w(on_sid_collision on_redis_down on_session_load_error).each do |h|
76
+ %w(on_redis_down on_session_load_error).each do |h|
65
77
  if (handler = public_send(h)) && !handler.respond_to?(:call)
66
78
  fail ArgumentError, "#{h} handler is not callable"
67
79
  end
@@ -72,19 +84,6 @@ class RedisSessionStore < ActionDispatch::Session::AbstractStore
72
84
  "#{default_options[:key_prefix]}#{sid}"
73
85
  end
74
86
 
75
- def generate_sid
76
- loop do
77
- sid = super
78
- break sid unless sid_collision?(sid)
79
- end
80
- end
81
-
82
- def sid_collision?(sid)
83
- !redis.setnx(prefixed(sid), nil).tap do |value|
84
- on_sid_collision.call(sid) if !value && on_sid_collision
85
- end
86
- end
87
-
88
87
  def get_session(env, sid)
89
88
  unless sid && (session = load_session_from_redis(sid))
90
89
  sid = generate_sid
@@ -163,6 +163,56 @@ describe RedisSessionStore do
163
163
  end
164
164
  end
165
165
 
166
+ describe 'checking for session existence' do
167
+ let(:session_id) { 'foo' }
168
+
169
+ before do
170
+ store.stub(:current_session_id).with(:env).and_return(session_id)
171
+ end
172
+
173
+ context 'when session id is not provided' do
174
+ context 'when session id is nil' do
175
+ let(:session_id) { nil }
176
+ it 'should return false' do
177
+ store.send(:session_exists?, :env).should be_false
178
+ end
179
+ end
180
+
181
+ context 'when session id is empty string' do
182
+ let(:session_id) { '' }
183
+ it 'should return false' do
184
+ store.stub(:current_session_id).with(:env).and_return('')
185
+ store.send(:session_exists?, :env).should be_false
186
+ end
187
+ end
188
+ end
189
+
190
+ context 'when session id is provided' do
191
+ let(:redis) { double('redis').tap { |o| store.stub(redis: o) } }
192
+
193
+ context 'when session id does not exist in redis' do
194
+ it 'should return false' do
195
+ redis.should_receive(:exists).with('foo').and_return(false)
196
+ store.send(:session_exists?, :env).should be_false
197
+ end
198
+ end
199
+
200
+ context 'when session id exists in redis' do
201
+ it 'should return true' do
202
+ redis.should_receive(:exists).with('foo').and_return(true)
203
+ store.send(:session_exists?, :env).should be_true
204
+ end
205
+ end
206
+
207
+ context 'when redis is down' do
208
+ it 'should return true (fallback to old behavior)' do
209
+ store.stub(:redis).and_raise(Errno::ECONNREFUSED)
210
+ store.send(:session_exists?, :env).should be_true
211
+ end
212
+ end
213
+ end
214
+ end
215
+
166
216
  describe 'fetching a session' do
167
217
  let :options do
168
218
  {
@@ -259,54 +309,6 @@ describe RedisSessionStore do
259
309
  end
260
310
  end
261
311
 
262
- describe 'generating a sid' do
263
- before { store.on_sid_collision = ->(sid) { @sid = sid } }
264
-
265
- context 'when the generated sid is unique' do
266
- before do
267
- redis = double('redis', setnx: true)
268
- store.stub(redis: redis)
269
- end
270
-
271
- it 'returns the sid' do
272
- expect(store.send(:generate_sid)).to_not be_nil
273
- end
274
-
275
- it 'does not pass the unique sid to the collision handler' do
276
- store.send(:sid_collision?, 'whatever')
277
- expect(@sid).to eql(nil)
278
- end
279
- end
280
-
281
- context 'when there is a generated sid collision' do
282
- before do
283
- redis = double('redis', setnx: false)
284
- store.stub(redis: redis)
285
- end
286
-
287
- it 'passes the colliding sid to the collision handler' do
288
- store.send(:sid_collision?, 'whatever')
289
- expect(@sid).to eql('whatever')
290
- end
291
- end
292
-
293
- it 'does not allow two processes to get the same sid' do
294
- redis = Redis.new
295
- store1 = RedisSessionStore.new(nil, options)
296
- store1.stub(redis: redis)
297
- store2 = RedisSessionStore.new(nil, options)
298
- store2.stub(redis: redis)
299
-
300
- # While this is stubbing out a method defined in spec/support.rb,
301
- # Rails does use SecureRandom for the random string
302
- store1.stub(:rand).and_return(1000)
303
- store2.stub(:rand).and_return(1000, 1001)
304
-
305
- expect(store1.send(:generate_sid)).to eq('3e8')
306
- expect(store2.send(:generate_sid)).to eq('3e9')
307
- end
308
- end
309
-
310
312
  describe 'session encoding' do
311
313
  let(:env) { double('env') }
312
314
  let(:session_id) { 12_345 }
@@ -447,7 +449,7 @@ describe RedisSessionStore do
447
449
  end
448
450
 
449
451
  describe 'validating custom handlers' do
450
- %w(on_redis_down on_sid_collision on_session_load_error).each do |h|
452
+ %w(on_redis_down on_session_load_error).each do |h|
451
453
  context 'when nil' do
452
454
  it 'does not explode at init' do
453
455
  expect { store }.to_not raise_error
metadata CHANGED
@@ -1,97 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-session-store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.6
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mathias Meyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-09 00:00:00.000000000 Z
11
+ date: 2014-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ! '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: fakeredis
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ! '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ! '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ! '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ! '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ! '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ! '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rubocop
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ! '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ! '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ! '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ! '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  description: A drop-in replacement for e.g. MemCacheStore to store Rails sessions
@@ -105,11 +105,11 @@ extra_rdoc_files:
105
105
  - AUTHORS.md
106
106
  - CONTRIBUTING.md
107
107
  files:
108
- - .gitignore
109
- - .rspec
110
- - .rubocop.yml
111
- - .simplecov
112
- - .travis.yml
108
+ - ".gitignore"
109
+ - ".rspec"
110
+ - ".rubocop.yml"
111
+ - ".simplecov"
112
+ - ".travis.yml"
113
113
  - AUTHORS.md
114
114
  - CHANGELOG.md
115
115
  - CONTRIBUTING.md
@@ -132,12 +132,12 @@ require_paths:
132
132
  - lib
133
133
  required_ruby_version: !ruby/object:Gem::Requirement
134
134
  requirements:
135
- - - ! '>='
135
+ - - ">="
136
136
  - !ruby/object:Gem::Version
137
137
  version: '0'
138
138
  required_rubygems_version: !ruby/object:Gem::Requirement
139
139
  requirements:
140
- - - ! '>='
140
+ - - ">="
141
141
  - !ruby/object:Gem::Version
142
142
  version: '0'
143
143
  requirements: []
@@ -148,3 +148,4 @@ specification_version: 4
148
148
  summary: A drop-in replacement for e.g. MemCacheStore to store Rails sessions (and
149
149
  Rails sessions only) in Redis.
150
150
  test_files: []
151
+ has_rdoc: true