redis-session-store 0.6.6 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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