redis-rack-my-checksub 2.1.3
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 +7 -0
- data/.github/auto-assign-issues.yml +2 -0
- data/.gitignore +6 -0
- data/.ruby-version +1 -0
- data/.travis.yml +23 -0
- data/CHANGELOG.md +246 -0
- data/CODEOWNERS +1 -0
- data/Gemfile +6 -0
- data/MIT-LICENSE +20 -0
- data/README.md +83 -0
- data/Rakefile +10 -0
- data/bin/appraisal +17 -0
- data/bin/rake +17 -0
- data/lib/rack/session/redis.rb +92 -0
- data/lib/redis-rack.rb +3 -0
- data/lib/redis/rack/connection.rb +50 -0
- data/lib/redis/rack/version.rb +5 -0
- data/redis-rack.gemspec +29 -0
- data/test/rack/session/redis_test.rb +414 -0
- data/test/redis/rack/connection_test.rb +84 -0
- data/test/test_helper.rb +5 -0
- metadata +182 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 90fb9bfcd8cd361bddd561256f85392471bdba3aaeac993f70bd77c7e277005e
|
|
4
|
+
data.tar.gz: 2ed881dcbe50724d3196ea95a89993a30480db7e56db16e5b7bcbe61fe3052b7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a47a0fbf5642ce628bf32368441f036e7786191162e32d5bb1801b087dfcdc0db04c8d5724d22d58e70665cec1f89c7fb3150fb567719a736c0c00f0403ded13
|
|
7
|
+
data.tar.gz: 38413bf90410051d323792a8b165928b7556f02a12e102c6a58442b8be34b2ad2c725bf77aa51d6b38d73793aecea0a63a559a994f24a2fe2deaf84783babeea
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.7.1
|
data/.travis.yml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
language: ruby
|
|
2
|
+
script: bundle exec rake
|
|
3
|
+
rvm:
|
|
4
|
+
- 2.2
|
|
5
|
+
- 2.3
|
|
6
|
+
- 2.4
|
|
7
|
+
- 2.5
|
|
8
|
+
- 2.6
|
|
9
|
+
- 2.7
|
|
10
|
+
- ruby-head
|
|
11
|
+
- jruby-head
|
|
12
|
+
matrix:
|
|
13
|
+
allow_failures:
|
|
14
|
+
- rvm: jruby-head
|
|
15
|
+
- rvm: ruby-head
|
|
16
|
+
deploy:
|
|
17
|
+
provider: rubygems
|
|
18
|
+
api_key:
|
|
19
|
+
secure: VTosVmCdLWUGK8KyzovYri7ySfd7fACtfL8MClEBBHnI+m2cLCpmtCJ7Z1X7z9BXlj974EDaF8V9iRKzfksXIf8aaPfVQw9AW94fLJZbfSB8YGOGyNbPu9YECoZQB1aZ2lw9s/aEdfwCbmqizO/fYpG3YoPKJdm1ZJpNOFR37Xk=
|
|
20
|
+
gem: redis-rack
|
|
21
|
+
on:
|
|
22
|
+
tags: true
|
|
23
|
+
repo: redis-store/redis-rack
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
v2.1.0 (2020-01-13)
|
|
2
|
+
--------------------------------------------------------------------------------
|
|
3
|
+
|
|
4
|
+
* Update version to v2.1.0 final
|
|
5
|
+
Tom Scott
|
|
6
|
+
|
|
7
|
+
* Test against most recent versions of Ruby
|
|
8
|
+
Tom Scott
|
|
9
|
+
|
|
10
|
+
* Update gemspec to remove rubyforge_project
|
|
11
|
+
|
|
12
|
+
It is deprecated or removed already
|
|
13
|
+
|
|
14
|
+
Original warning message:
|
|
15
|
+
Gem::Specification#rubyforge_project= is deprecated with no replacement. It will be removed on or after 2019-12-01.
|
|
16
|
+
PikachuEXE
|
|
17
|
+
|
|
18
|
+
* back tests to original state
|
|
19
|
+
Alexey Vasiliev
|
|
20
|
+
|
|
21
|
+
* Fix redis-rack for rack 2.0.8, small improvements
|
|
22
|
+
Alexey Vasiliev
|
|
23
|
+
|
|
24
|
+
* Fix redis-rack for rack 2.0.8, fix generate_unique_sid
|
|
25
|
+
Alexey Vasiliev
|
|
26
|
+
|
|
27
|
+
* Fix redis-rack for rack 2.0.8
|
|
28
|
+
Alexey Vasiliev
|
|
29
|
+
|
|
30
|
+
* v2.1.0.pre: Update Rack Session API, drop support for Rack 1.0
|
|
31
|
+
Tom Scott
|
|
32
|
+
|
|
33
|
+
v2.0.6
|
|
34
|
+
--------------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
* v2.0.6: Prevent Connection Pool from passing nil options
|
|
37
|
+
Tom Scott
|
|
38
|
+
|
|
39
|
+
* Make bundler dependency more permissive to support future ruby versions
|
|
40
|
+
Tom Scott
|
|
41
|
+
|
|
42
|
+
* Drop support for Rack v1
|
|
43
|
+
Tom Scott
|
|
44
|
+
|
|
45
|
+
* Update Rack::Session API Compatibility
|
|
46
|
+
|
|
47
|
+
The latest version of Rack [deprecated subclassing Rack::Session::ID](https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L412-L419),
|
|
48
|
+
replacing it instead with `Rack::Session::Persisted`. Update the method
|
|
49
|
+
implementations in this gem to support the new API. Shoutouts to @onk
|
|
50
|
+
for actually writing this code, we're pulling it in from his fork.
|
|
51
|
+
Tom Scott
|
|
52
|
+
|
|
53
|
+
* Automatically assign issues to @tubbo when they are created
|
|
54
|
+
Tom Scott
|
|
55
|
+
|
|
56
|
+
* Automatically release to RubyGems when new tags are pushed to GitHub
|
|
57
|
+
Tom Scott
|
|
58
|
+
|
|
59
|
+
* Raise Error when `:redis_store` is Incorrect Type
|
|
60
|
+
|
|
61
|
+
Passing an object that isn't a `Redis::Store` into the session storage
|
|
62
|
+
configuration can cause errors when methods are called with more
|
|
63
|
+
arguments than the default Redis client can handle. These additional
|
|
64
|
+
arguments are used by `Rack::Session::Redis` to configure the key
|
|
65
|
+
namespace and TTL globally in the main configuration, and pass them down
|
|
66
|
+
to `Redis::Store` to be appended to the key. An `ArgumentError` will now
|
|
67
|
+
be thrown when a `:redis_store` is of a type that isn't a `Redis::Store`
|
|
68
|
+
(or a subclass thereof).
|
|
69
|
+
|
|
70
|
+
Resolves #46
|
|
71
|
+
Tom Scott
|
|
72
|
+
|
|
73
|
+
* Add code owners
|
|
74
|
+
Tom Scott
|
|
75
|
+
|
|
76
|
+
* Do not provide nil values for missing connection pool options
|
|
77
|
+
|
|
78
|
+
Fixes #44
|
|
79
|
+
Jake Goulding
|
|
80
|
+
|
|
81
|
+
v2.0.5
|
|
82
|
+
--------------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
* v2.0.5 release
|
|
85
|
+
Tom Scott
|
|
86
|
+
|
|
87
|
+
* add spec to validate blank sessions are not stored in redis
|
|
88
|
+
mstruve
|
|
89
|
+
|
|
90
|
+
* dont store a blank session in redis
|
|
91
|
+
mstruve
|
|
92
|
+
|
|
93
|
+
* Add documentation for options
|
|
94
|
+
Daniel M Barlow
|
|
95
|
+
|
|
96
|
+
v2.0.4
|
|
97
|
+
--------------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
* Release 2.0.4
|
|
100
|
+
Tom Scott
|
|
101
|
+
|
|
102
|
+
* Remove .ruby-version
|
|
103
|
+
Tom Scott
|
|
104
|
+
|
|
105
|
+
* Remove rake, appraisal from gemspec executables
|
|
106
|
+
|
|
107
|
+
The contents of the bin directory are binstubs to assist development.
|
|
108
|
+
They are not intended to be shipped with the gem itself. This commit
|
|
109
|
+
updates the gemspec to ensure that they are not exposed to rubygems as
|
|
110
|
+
executables for redis-rack, which will fix conflicts with the legitimate
|
|
111
|
+
rake and appraisal executables provided by those other gems.
|
|
112
|
+
Matt Brictson
|
|
113
|
+
|
|
114
|
+
v2.0.3
|
|
115
|
+
--------------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
* v2.0.3: Avoid mutex locking with global option
|
|
118
|
+
Tom Scott
|
|
119
|
+
|
|
120
|
+
* Restore default_options in #generate_unique_sid
|
|
121
|
+
Tom Scott
|
|
122
|
+
|
|
123
|
+
* Refactor connection_pool code
|
|
124
|
+
|
|
125
|
+
Adds a `Redis::Rack::Connection` object for handling the instantiation
|
|
126
|
+
of a `Redis::Store` or a `ConnectionPool` if pool configuration is
|
|
127
|
+
given. Restores the purpose of `Rack::Session::Redis` to only implement
|
|
128
|
+
the session ID abstract API provided by Rack.
|
|
129
|
+
Tom Scott
|
|
130
|
+
|
|
131
|
+
* Rename :use_global_lock option to :threadsafe to match Rails
|
|
132
|
+
Garrett Thornburg
|
|
133
|
+
|
|
134
|
+
* Use mocha mocks to assert the mutex is never locked
|
|
135
|
+
Garrett Thornburg
|
|
136
|
+
|
|
137
|
+
* Allow redis-store v1.4
|
|
138
|
+
Shane O'Grady
|
|
139
|
+
|
|
140
|
+
* Create a :use_global_lock option that avoids the global lock
|
|
141
|
+
Garrett Thornburg
|
|
142
|
+
|
|
143
|
+
* Rake task for running all tests on all gem versions
|
|
144
|
+
Tom Scott
|
|
145
|
+
|
|
146
|
+
* Install Appraisal so we can test against multiple versions of Rack
|
|
147
|
+
Tom Scott
|
|
148
|
+
|
|
149
|
+
v2.0.2
|
|
150
|
+
--------------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
* v2.0.2: Resolve regression forcing Rack 2.x and above
|
|
153
|
+
Tom Scott
|
|
154
|
+
|
|
155
|
+
v2.0.1
|
|
156
|
+
--------------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
* v2.0.1: Relax gem dependencies
|
|
159
|
+
Tom Scott
|
|
160
|
+
|
|
161
|
+
* smoothen redis-store dependency
|
|
162
|
+
Mathieu Jobin
|
|
163
|
+
|
|
164
|
+
* Drop support for Rubinius 1.9 mode
|
|
165
|
+
|
|
166
|
+
1.9.x is EOL in MRI Ruby Land, and Rubinius 1.9 mode isn't really used
|
|
167
|
+
much anymore. This should make the builds pass again.
|
|
168
|
+
Tom Scott
|
|
169
|
+
|
|
170
|
+
* Update README.md (#20)
|
|
171
|
+
Nicolas
|
|
172
|
+
|
|
173
|
+
* Remove jruby 1.9 mode from the build matrix
|
|
174
|
+
Tom Scott
|
|
175
|
+
|
|
176
|
+
* Remove test because why
|
|
177
|
+
Tom Scott
|
|
178
|
+
|
|
179
|
+
v2.0.0
|
|
180
|
+
--------------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
* v2.0: Drop support for Ruby below 2.2 (thanks @connorshea)
|
|
183
|
+
|
|
184
|
+
Fixes #17, major shoutouts to @connorshea for getting this off the
|
|
185
|
+
ground. This also edits the CI config to drop support for older Ruby
|
|
186
|
+
versions.
|
|
187
|
+
Tom Scott
|
|
188
|
+
|
|
189
|
+
* Fix gem dependency versions
|
|
190
|
+
Tom Scott
|
|
191
|
+
|
|
192
|
+
* v2.0.0.pre Add support for Rails 5 and Rack 2.
|
|
193
|
+
Tom Scott
|
|
194
|
+
|
|
195
|
+
* v2.0.0: Upgrade to Rack 2.0
|
|
196
|
+
|
|
197
|
+
This release includes some backwards-incompatible changes that pertain
|
|
198
|
+
to Rails' upgrade to Rack 2.x.
|
|
199
|
+
Tom Scott
|
|
200
|
+
|
|
201
|
+
* Update README
|
|
202
|
+
Tom Scott
|
|
203
|
+
|
|
204
|
+
* Fix readme
|
|
205
|
+
Tom Scott
|
|
206
|
+
|
|
207
|
+
* travis.yml add Ruby 2.3.0
|
|
208
|
+
shiro16
|
|
209
|
+
|
|
210
|
+
* add 2.1 and 2.2 rubies to travis
|
|
211
|
+
Marc Roberts
|
|
212
|
+
|
|
213
|
+
* Remove support for Ruby 1.9
|
|
214
|
+
Marc Roberts
|
|
215
|
+
|
|
216
|
+
* Loosen dependancy on Rack to allow 1.5 and 2.x
|
|
217
|
+
|
|
218
|
+
Rails 5 beta requires Rack 2.x and this gem is unusable unless can use Rack 2.x
|
|
219
|
+
Marc Roberts
|
|
220
|
+
|
|
221
|
+
* Update README.md
|
|
222
|
+
Ryan Bigg
|
|
223
|
+
|
|
224
|
+
* add support for ConnectionPool
|
|
225
|
+
Roman Usherenko
|
|
226
|
+
|
|
227
|
+
* Introduce redis_store option (closes #1)
|
|
228
|
+
Kurakin Alexander
|
|
229
|
+
|
|
230
|
+
* Atomically lock session id and implement skip support.
|
|
231
|
+
W. Andrew Loe III
|
|
232
|
+
|
|
233
|
+
v1.5.0
|
|
234
|
+
------
|
|
235
|
+
|
|
236
|
+
* Enable CI
|
|
237
|
+
Luca Guidi
|
|
238
|
+
|
|
239
|
+
* Update README.md
|
|
240
|
+
Luca Guidi
|
|
241
|
+
|
|
242
|
+
* Moved back from jodosha/redis-store
|
|
243
|
+
Luca Guidi
|
|
244
|
+
|
|
245
|
+
* Moved
|
|
246
|
+
Luca Guidi
|
data/CODEOWNERS
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @tubbo
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 - 2013 Luca Guidi
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Redis session store for Rack
|
|
2
|
+
|
|
3
|
+
__`redis-rack`__ provides a Redis-backed session store for __Rack__.
|
|
4
|
+
|
|
5
|
+
See the main [redis-store readme] for general guidelines.
|
|
6
|
+
|
|
7
|
+
**NOTE:** This is not [redis-rack-cache][], the library for using Redis
|
|
8
|
+
as a backend store for the `Rack::Cache` HTTP cache. All this gem does
|
|
9
|
+
is store the Rack session within Redis.
|
|
10
|
+
|
|
11
|
+
[](http://travis-ci.org/redis-store/redis-rack?branch=master)
|
|
12
|
+
[](https://codeclimate.com/github/redis-store/redis-rack)
|
|
13
|
+
[](http://badge.fury.io/rb/redis-rack)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Install with Bundler by adding the following to Gemfile:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
gem 'redis-rack'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then, run:
|
|
24
|
+
|
|
25
|
+
```shell
|
|
26
|
+
$ bundle install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or, you can install it manually using RubyGems:
|
|
30
|
+
|
|
31
|
+
```shell
|
|
32
|
+
$ gem install redis-rack
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
If you are using redis-store with Rails, consider using the [redis-rails gem](https://github.com/redis-store/redis-rails) instead. For standalone usage:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
# config.ru
|
|
41
|
+
require 'rack'
|
|
42
|
+
require 'rack/session/redis'
|
|
43
|
+
|
|
44
|
+
use Rack::Session::Redis
|
|
45
|
+
|
|
46
|
+
# Alternatively you can specify options to use:
|
|
47
|
+
use Rack::Session::Redis,
|
|
48
|
+
:redis_server => "redis://redis:6379/0",
|
|
49
|
+
:expires_in => 3600 # Seconds. If you are using ActiveSupport you can use 1.hour
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
To install this gem for development purposes:
|
|
55
|
+
|
|
56
|
+
```shell
|
|
57
|
+
$ gem install bundler # note: you don't need to do this if you already have it installed
|
|
58
|
+
$ git clone git://github.com/redis-store/redis-rack.git
|
|
59
|
+
$ cd redis-rack
|
|
60
|
+
$ bundle install
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Running tests
|
|
64
|
+
|
|
65
|
+
To run tests:
|
|
66
|
+
|
|
67
|
+
```shell
|
|
68
|
+
$ bundle exec rake
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If you are on **Snow Leopard** you have to run the following command to
|
|
72
|
+
build this software:
|
|
73
|
+
|
|
74
|
+
```shell
|
|
75
|
+
$ env ARCHFLAGS="-arch x86_64" bundle exec rake
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Copyright
|
|
79
|
+
|
|
80
|
+
2009 - 2013 Luca Guidi - [http://lucaguidi.com](http://lucaguidi.com), released under the MIT license
|
|
81
|
+
|
|
82
|
+
[redis-rack-cache]: https://github.com/redis-store/redis-rack-cache
|
|
83
|
+
[redis-store readme]: https://github.com/redis-store/redis-store
|
data/Rakefile
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require 'bundler/setup'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'redis-store/testing/tasks'
|
|
5
|
+
|
|
6
|
+
task :all do
|
|
7
|
+
Dir["gemfiles/*.gemfile"].reject { |p| p =~ /\.lock\Z/ }.each do |gemfile|
|
|
8
|
+
sh "BUNDLE_GEMFILE=#{gemfile} bundle exec rake"
|
|
9
|
+
end
|
|
10
|
+
end
|
data/bin/appraisal
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# This file was generated by Bundler.
|
|
5
|
+
#
|
|
6
|
+
# The application 'appraisal' is installed as part of a gem, and
|
|
7
|
+
# this file is here to facilitate running it.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
require "pathname"
|
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
12
|
+
Pathname.new(__FILE__).realpath)
|
|
13
|
+
|
|
14
|
+
require "rubygems"
|
|
15
|
+
require "bundler/setup"
|
|
16
|
+
|
|
17
|
+
load Gem.bin_path("appraisal", "appraisal")
|
data/bin/rake
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# This file was generated by Bundler.
|
|
5
|
+
#
|
|
6
|
+
# The application 'rake' is installed as part of a gem, and
|
|
7
|
+
# this file is here to facilitate running it.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
require "pathname"
|
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
12
|
+
Pathname.new(__FILE__).realpath)
|
|
13
|
+
|
|
14
|
+
require "rubygems"
|
|
15
|
+
require "bundler/setup"
|
|
16
|
+
|
|
17
|
+
load Gem.bin_path("rake", "rake")
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'rack/session/abstract/id'
|
|
2
|
+
require 'redis-store'
|
|
3
|
+
require 'thread'
|
|
4
|
+
require 'redis/rack/connection'
|
|
5
|
+
|
|
6
|
+
module Rack
|
|
7
|
+
module Session
|
|
8
|
+
class Redis < Abstract::PersistedSecure
|
|
9
|
+
attr_reader :mutex
|
|
10
|
+
|
|
11
|
+
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge(
|
|
12
|
+
:redis_server => 'redis://127.0.0.1:6379/0/rack:session'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def initialize(app, options = {})
|
|
16
|
+
super
|
|
17
|
+
|
|
18
|
+
@mutex = Mutex.new
|
|
19
|
+
@conn = ::Redis::Rack::Connection.new(@default_options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def generate_unique_sid(session)
|
|
23
|
+
return generate_sid if session.empty?
|
|
24
|
+
loop do
|
|
25
|
+
sid = generate_sid
|
|
26
|
+
first = with do |c|
|
|
27
|
+
[*c.setnx(sid.private_id, session, @default_options.to_hash)].first
|
|
28
|
+
end
|
|
29
|
+
break sid if [1, true].include?(first)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def find_session(req, sid)
|
|
34
|
+
if req.session.options[:skip]
|
|
35
|
+
[generate_sid, {}]
|
|
36
|
+
else
|
|
37
|
+
with_lock(req, [nil, {}]) do
|
|
38
|
+
unless sid and session = get_session_with_fallback(sid)
|
|
39
|
+
session = {}
|
|
40
|
+
sid = generate_unique_sid(session)
|
|
41
|
+
end
|
|
42
|
+
[sid, session]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def write_session(req, sid, new_session, options = {})
|
|
48
|
+
with_lock(req, false) do
|
|
49
|
+
with { |c| c.set(sid.private_id, new_session, options.to_hash) }
|
|
50
|
+
sid
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def delete_session(req, sid, options)
|
|
55
|
+
with_lock(req) do
|
|
56
|
+
with do |c|
|
|
57
|
+
c.del(sid.public_id)
|
|
58
|
+
c.del(sid.private_id)
|
|
59
|
+
end
|
|
60
|
+
generate_sid unless options[:drop]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def threadsafe?
|
|
65
|
+
@default_options.fetch(:threadsafe, true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def with_lock(req, default = nil)
|
|
69
|
+
@mutex.lock if req.multithread? && threadsafe?
|
|
70
|
+
yield
|
|
71
|
+
rescue Errno::ECONNREFUSED
|
|
72
|
+
if $VERBOSE
|
|
73
|
+
warn "#{self} is unable to find Redis server."
|
|
74
|
+
warn $!.inspect
|
|
75
|
+
end
|
|
76
|
+
default
|
|
77
|
+
ensure
|
|
78
|
+
@mutex.unlock if @mutex.locked?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def with(&block)
|
|
82
|
+
@conn.with(&block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def get_session_with_fallback(sid)
|
|
88
|
+
with { |c| c.get(sid.private_id) || c.get(sid.public_id) }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
data/lib/redis-rack.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
class Redis
|
|
2
|
+
module Rack
|
|
3
|
+
class Connection
|
|
4
|
+
POOL_KEYS = %i[pool pool_size pool_timeout].freeze
|
|
5
|
+
|
|
6
|
+
def initialize(options = {})
|
|
7
|
+
@options = options
|
|
8
|
+
@store = options[:redis_store]
|
|
9
|
+
@pool = options[:pool]
|
|
10
|
+
|
|
11
|
+
if @pool && !@pool.is_a?(ConnectionPool)
|
|
12
|
+
raise ArgumentError, "pool must be an instance of ConnectionPool"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
if @store && !@store.is_a?(Redis::Store)
|
|
16
|
+
raise ArgumentError, "redis_store must be an instance of Redis::Store (currently #{@store.class.name})"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def with(&block)
|
|
21
|
+
if pooled?
|
|
22
|
+
pool.with(&block)
|
|
23
|
+
else
|
|
24
|
+
block.call(store)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def pooled?
|
|
29
|
+
return @pooled if defined?(@pooled)
|
|
30
|
+
|
|
31
|
+
@pooled = POOL_KEYS.any? { |key| @options.key?(key) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def pool
|
|
35
|
+
@pool ||= ConnectionPool.new(pool_options) { store } if pooled?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def store
|
|
39
|
+
@store ||= Redis::Store::Factory.create(@options[:redis_server], ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def pool_options
|
|
43
|
+
{
|
|
44
|
+
size: @options[:pool_size],
|
|
45
|
+
timeout: @options[:pool_timeout]
|
|
46
|
+
}.reject { |key, value| value.nil? }.to_h
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/redis-rack.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
|
3
|
+
require 'redis/rack/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = 'redis-rack-my-checksub'
|
|
7
|
+
s.version = Redis::Rack::VERSION
|
|
8
|
+
s.authors = ['Luca Guidi']
|
|
9
|
+
s.email = ['me@lucaguidi.com']
|
|
10
|
+
s.homepage = 'http://redis-store.org/redis-rack'
|
|
11
|
+
s.summary = %q{Redis Store for Rack}
|
|
12
|
+
s.description = %q{Redis Store for Rack applications}
|
|
13
|
+
s.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
s.files = `git ls-files`.split("\n")
|
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
17
|
+
s.executables = []
|
|
18
|
+
s.require_paths = ["lib"]
|
|
19
|
+
|
|
20
|
+
s.add_runtime_dependency 'redis-store', ['< 2', '>= 1.2']
|
|
21
|
+
s.add_runtime_dependency 'rack', '>= 2.0.8', '< 3'
|
|
22
|
+
|
|
23
|
+
s.add_development_dependency 'rake', '>= 12.3.3'
|
|
24
|
+
s.add_development_dependency 'bundler', '> 1', '< 3'
|
|
25
|
+
s.add_development_dependency 'mocha', '~> 0.14.0'
|
|
26
|
+
s.add_development_dependency 'minitest', '~> 5'
|
|
27
|
+
s.add_development_dependency 'connection_pool', '~> 1.2.0'
|
|
28
|
+
end
|
|
29
|
+
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'rack/mock'
|
|
3
|
+
require 'thread'
|
|
4
|
+
require 'connection_pool'
|
|
5
|
+
|
|
6
|
+
describe Rack::Session::Redis do
|
|
7
|
+
session_key = Rack::Session::Redis::DEFAULT_OPTIONS[:key]
|
|
8
|
+
session_match = /#{session_key}=([0-9a-fA-F]+);/
|
|
9
|
+
incrementor = lambda do |env|
|
|
10
|
+
env["rack.session"]["counter"] ||= 0
|
|
11
|
+
env["rack.session"]["counter"] += 1
|
|
12
|
+
Rack::Response.new(env["rack.session"].inspect).to_a
|
|
13
|
+
end
|
|
14
|
+
drop_session = proc do |env|
|
|
15
|
+
env['rack.session.options'][:drop] = true
|
|
16
|
+
incrementor.call(env)
|
|
17
|
+
end
|
|
18
|
+
renew_session = proc do |env|
|
|
19
|
+
env['rack.session.options'][:renew] = true
|
|
20
|
+
incrementor.call(env)
|
|
21
|
+
end
|
|
22
|
+
defer_session = proc do |env|
|
|
23
|
+
env['rack.session.options'][:defer] = true
|
|
24
|
+
incrementor.call(env)
|
|
25
|
+
end
|
|
26
|
+
skip_session = proc do |env|
|
|
27
|
+
env['rack.session.options'][:skip] = true
|
|
28
|
+
incrementor.call(env)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# # test Redis connection
|
|
32
|
+
# Rack::Session::Redis.new(incrementor)
|
|
33
|
+
#
|
|
34
|
+
# it "faults on no connection" do
|
|
35
|
+
# lambda{
|
|
36
|
+
# Rack::Session::Redis.new(incrementor, :redis_server => 'nosuchserver')
|
|
37
|
+
# }.must_raise(Exception)
|
|
38
|
+
# end
|
|
39
|
+
|
|
40
|
+
it "can create it's own pool" do
|
|
41
|
+
session_store = Rack::Session::Redis.new(incrementor, pool_size: 5, pool_timeout: 10)
|
|
42
|
+
conn = session_store.instance_variable_get(:@conn)
|
|
43
|
+
conn.pool.class.must_equal ::ConnectionPool
|
|
44
|
+
conn.pool.instance_variable_get(:@size).must_equal 5
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "can create it's own pool using default Redis server" do
|
|
48
|
+
session_store = Rack::Session::Redis.new(incrementor, pool_size: 5, pool_timeout: 10)
|
|
49
|
+
session_store.with { |connection| connection.to_s.must_match(/127\.0\.0\.1:6379 against DB 0 with namespace rack:session$/) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "can create it's own pool using provided Redis server" do
|
|
53
|
+
session_store = Rack::Session::Redis.new(incrementor, redis_server: 'redis://127.0.0.1:6380/1', pool_size: 5, pool_timeout: 10)
|
|
54
|
+
session_store.with { |connection| connection.to_s.must_match(/127\.0\.0\.1:6380 against DB 1$/) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "can use a supplied pool" do
|
|
58
|
+
session_store = Rack::Session::Redis.new(incrementor, pool: ::ConnectionPool.new(size: 1, timeout: 1) { ::Redis::Store::Factory.create("redis://127.0.0.1:6380/1")})
|
|
59
|
+
conn = session_store.instance_variable_get(:@conn)
|
|
60
|
+
conn.pool.class.must_equal ::ConnectionPool
|
|
61
|
+
conn.pool.instance_variable_get(:@size).must_equal 1
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "uses the specified Redis store when provided" do
|
|
65
|
+
store = ::Redis::Store::Factory.create('redis://127.0.0.1:6380/1')
|
|
66
|
+
pool = Rack::Session::Redis.new(incrementor, :redis_store => store)
|
|
67
|
+
pool.with do |p|
|
|
68
|
+
p.to_s.must_match(/127\.0\.0\.1:6380 against DB 1$/)
|
|
69
|
+
p.must_equal(store)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "uses the default Redis server and namespace when not provided" do
|
|
74
|
+
pool = Rack::Session::Redis.new(incrementor)
|
|
75
|
+
pool.with { |p| p.to_s.must_match(/127\.0\.0\.1:6379 against DB 0 with namespace rack:session$/) }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "uses the specified namespace when provided" do
|
|
79
|
+
pool = Rack::Session::Redis.new(incrementor, :redis_server => {:namespace => 'test:rack:session'})
|
|
80
|
+
pool.with { |p| p.to_s.must_match(/namespace test:rack:session$/) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "uses the specified Redis server when provided" do
|
|
84
|
+
pool = Rack::Session::Redis.new(incrementor, :redis_server => 'redis://127.0.0.1:6380/1')
|
|
85
|
+
pool.with { |p| p.to_s.must_match(/127\.0\.0\.1:6380 against DB 1$/) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "is threadsafe by default" do
|
|
89
|
+
sesion_store = Rack::Session::Redis.new(incrementor)
|
|
90
|
+
sesion_store.threadsafe?.must_equal(true)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "does not store a blank session" do
|
|
94
|
+
session_store = Rack::Session::Redis.new(incrementor)
|
|
95
|
+
sid = session_store.generate_unique_sid({})
|
|
96
|
+
session_store.with { |c| c.get(sid.private_id).must_be_nil }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "locks the store mutex" do
|
|
100
|
+
mutex = Mutex.new
|
|
101
|
+
mutex.expects(:lock).once
|
|
102
|
+
sesion_store = Rack::Session::Redis.new(incrementor)
|
|
103
|
+
sesion_store.instance_variable_set(:@mutex, mutex)
|
|
104
|
+
was_yielded = false
|
|
105
|
+
request = Minitest::Mock.new
|
|
106
|
+
request.expect(:multithread?, true)
|
|
107
|
+
sesion_store.with_lock(request) { was_yielded = true}
|
|
108
|
+
was_yielded.must_equal(true)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe "threadsafe disabled" do
|
|
112
|
+
it "can have the global lock disabled" do
|
|
113
|
+
sesion_store = Rack::Session::Redis.new(incrementor, :threadsafe => false)
|
|
114
|
+
sesion_store.threadsafe?.must_equal(false)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "does not lock the store mutex" do
|
|
118
|
+
mutex = Mutex.new
|
|
119
|
+
mutex.expects(:lock).never
|
|
120
|
+
sesion_store = Rack::Session::Redis.new(incrementor, :threadsafe => false)
|
|
121
|
+
sesion_store.instance_variable_set(:@mutex, mutex)
|
|
122
|
+
was_yielded = false
|
|
123
|
+
request = Minitest::Mock.new
|
|
124
|
+
request.expect(:multithread?, true)
|
|
125
|
+
sesion_store.with_lock(request) { was_yielded = true}
|
|
126
|
+
was_yielded.must_equal(true)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "creates a new cookie" do
|
|
131
|
+
with_pool_management(incrementor) do |pool|
|
|
132
|
+
res = Rack::MockRequest.new(pool).get("/")
|
|
133
|
+
res["Set-Cookie"].must_include("#{session_key}=")
|
|
134
|
+
res.body.must_equal('{"counter"=>1}')
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "determines session from a cookie" do
|
|
139
|
+
with_pool_management(incrementor) do |pool|
|
|
140
|
+
req = Rack::MockRequest.new(pool)
|
|
141
|
+
res = req.get("/")
|
|
142
|
+
cookie = res["Set-Cookie"]
|
|
143
|
+
req.get("/", "HTTP_COOKIE" => cookie).
|
|
144
|
+
body.must_equal('{"counter"=>2}')
|
|
145
|
+
req.get("/", "HTTP_COOKIE" => cookie).
|
|
146
|
+
body.must_equal('{"counter"=>3}')
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "determines session only from a cookie by default" do
|
|
151
|
+
with_pool_management(incrementor) do |pool|
|
|
152
|
+
req = Rack::MockRequest.new(pool)
|
|
153
|
+
res = req.get("/")
|
|
154
|
+
sid = res["Set-Cookie"][session_match, 1]
|
|
155
|
+
req.get("/?rack.session=#{sid}").
|
|
156
|
+
body.must_equal('{"counter"=>1}')
|
|
157
|
+
req.get("/?rack.session=#{sid}").
|
|
158
|
+
body.must_equal('{"counter"=>1}')
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "determines session from params" do
|
|
163
|
+
with_pool_management(incrementor, :cookie_only => false) do |pool|
|
|
164
|
+
req = Rack::MockRequest.new(pool)
|
|
165
|
+
res = req.get("/")
|
|
166
|
+
sid = res["Set-Cookie"][session_match, 1]
|
|
167
|
+
req.get("/?rack.session=#{sid}").
|
|
168
|
+
body.must_equal('{"counter"=>2}')
|
|
169
|
+
req.get("/?rack.session=#{sid}").
|
|
170
|
+
body.must_equal('{"counter"=>3}')
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "survives nonexistant cookies" do
|
|
175
|
+
bad_cookie = "rack.session=blarghfasel"
|
|
176
|
+
with_pool_management(incrementor) do |pool|
|
|
177
|
+
res = Rack::MockRequest.new(pool).
|
|
178
|
+
get("/", "HTTP_COOKIE" => bad_cookie)
|
|
179
|
+
res.body.must_equal('{"counter"=>1}')
|
|
180
|
+
cookie = res["Set-Cookie"][session_match]
|
|
181
|
+
cookie.wont_match(/#{bad_cookie}/)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it "maintains freshness" do
|
|
186
|
+
with_pool_management(incrementor, :expire_after => 3) do |pool|
|
|
187
|
+
res = Rack::MockRequest.new(pool).get('/')
|
|
188
|
+
res.body.must_include('"counter"=>1')
|
|
189
|
+
cookie = res["Set-Cookie"]
|
|
190
|
+
sid = cookie[session_match, 1]
|
|
191
|
+
res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
|
|
192
|
+
res["Set-Cookie"][session_match, 1].must_equal(sid)
|
|
193
|
+
res.body.must_include('"counter"=>2')
|
|
194
|
+
puts 'Sleeping to expire session' if $DEBUG
|
|
195
|
+
sleep 4
|
|
196
|
+
res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
|
|
197
|
+
res["Set-Cookie"][session_match, 1].wont_equal(sid)
|
|
198
|
+
res.body.must_include('"counter"=>1')
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "does not send the same session id if it did not change" do
|
|
203
|
+
with_pool_management(incrementor) do |pool|
|
|
204
|
+
req = Rack::MockRequest.new(pool)
|
|
205
|
+
|
|
206
|
+
res0 = req.get("/")
|
|
207
|
+
cookie = res0["Set-Cookie"]
|
|
208
|
+
res0.body.must_equal('{"counter"=>1}')
|
|
209
|
+
|
|
210
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
|
211
|
+
res1["Set-Cookie"].must_be_nil
|
|
212
|
+
res1.body.must_equal('{"counter"=>2}')
|
|
213
|
+
|
|
214
|
+
res2 = req.get("/", "HTTP_COOKIE" => cookie)
|
|
215
|
+
res2["Set-Cookie"].must_be_nil
|
|
216
|
+
res2.body.must_equal('{"counter"=>3}')
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it "deletes cookies with :drop option" do
|
|
221
|
+
with_pool_management(incrementor) do |pool|
|
|
222
|
+
req = Rack::MockRequest.new(pool)
|
|
223
|
+
drop = Rack::Utils::Context.new(pool, drop_session)
|
|
224
|
+
dreq = Rack::MockRequest.new(drop)
|
|
225
|
+
|
|
226
|
+
res1 = req.get("/")
|
|
227
|
+
session = (cookie = res1["Set-Cookie"])[session_match]
|
|
228
|
+
res1.body.must_equal('{"counter"=>1}')
|
|
229
|
+
|
|
230
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
|
|
231
|
+
res2["Set-Cookie"].must_be_nil
|
|
232
|
+
res2.body.must_equal('{"counter"=>2}')
|
|
233
|
+
|
|
234
|
+
res3 = req.get("/", "HTTP_COOKIE" => cookie)
|
|
235
|
+
res3["Set-Cookie"][session_match].wont_equal(session)
|
|
236
|
+
res3.body.must_equal('{"counter"=>1}')
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "provides new session id with :renew option" do
|
|
241
|
+
with_pool_management(incrementor) do |pool|
|
|
242
|
+
req = Rack::MockRequest.new(pool)
|
|
243
|
+
renew = Rack::Utils::Context.new(pool, renew_session)
|
|
244
|
+
rreq = Rack::MockRequest.new(renew)
|
|
245
|
+
|
|
246
|
+
res1 = req.get("/")
|
|
247
|
+
session = (cookie = res1["Set-Cookie"])[session_match]
|
|
248
|
+
res1.body.must_equal('{"counter"=>1}')
|
|
249
|
+
|
|
250
|
+
res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
|
|
251
|
+
new_cookie = res2["Set-Cookie"]
|
|
252
|
+
new_session = new_cookie[session_match]
|
|
253
|
+
new_session.wont_equal(session)
|
|
254
|
+
res2.body.must_equal('{"counter"=>2}')
|
|
255
|
+
|
|
256
|
+
res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
|
|
257
|
+
res3.body.must_equal('{"counter"=>3}')
|
|
258
|
+
|
|
259
|
+
# Old cookie was deleted
|
|
260
|
+
res4 = req.get("/", "HTTP_COOKIE" => cookie)
|
|
261
|
+
res4.body.must_equal('{"counter"=>1}')
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "omits cookie with :defer option" do
|
|
266
|
+
with_pool_management(incrementor) do |pool|
|
|
267
|
+
defer = Rack::Utils::Context.new(pool, defer_session)
|
|
268
|
+
dreq = Rack::MockRequest.new(defer)
|
|
269
|
+
|
|
270
|
+
res0 = dreq.get("/")
|
|
271
|
+
res0["Set-Cookie"].must_be_nil
|
|
272
|
+
res0.body.must_equal('{"counter"=>1}')
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "does not hit with :skip option" do
|
|
277
|
+
with_pool_management(incrementor) do |session_store|
|
|
278
|
+
skip = Rack::Utils::Context.new(session_store, skip_session)
|
|
279
|
+
sreq = Rack::MockRequest.new(skip)
|
|
280
|
+
|
|
281
|
+
res0 = sreq.get("/")
|
|
282
|
+
res0.body.must_equal('{"counter"=>1}')
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
it "updates deep hashes correctly" do
|
|
287
|
+
hash_check = proc do |env|
|
|
288
|
+
session = env['rack.session']
|
|
289
|
+
unless session.include? 'test'
|
|
290
|
+
session.update :a => :b, :c => { :d => :e },
|
|
291
|
+
:f => { :g => { :h => :i} }, 'test' => true
|
|
292
|
+
else
|
|
293
|
+
session[:f][:g][:h] = :j
|
|
294
|
+
end
|
|
295
|
+
[200, {}, [session.inspect]]
|
|
296
|
+
end
|
|
297
|
+
with_pool_management(hash_check) do |pool|
|
|
298
|
+
req = Rack::MockRequest.new(pool)
|
|
299
|
+
|
|
300
|
+
res0 = req.get("/")
|
|
301
|
+
session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
|
|
302
|
+
sid = Rack::Session::SessionId.new(session_id)
|
|
303
|
+
ses0 = pool.with { |c| c.get(sid.private_id) }
|
|
304
|
+
|
|
305
|
+
req.get("/", "HTTP_COOKIE" => cookie)
|
|
306
|
+
ses1 = pool.with { |c| c.get(sid.private_id) }
|
|
307
|
+
|
|
308
|
+
ses1.wont_equal(ses0)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# anyone know how to do this better?
|
|
313
|
+
it "cleanly merges sessions when multithreaded" do
|
|
314
|
+
unless $DEBUG
|
|
315
|
+
1.must_equal(1) # fake assertion to appease the mighty bacon
|
|
316
|
+
next
|
|
317
|
+
end
|
|
318
|
+
warn 'Running multithread test for Session::Redis'
|
|
319
|
+
with_pool_management(incrementor) do |pool|
|
|
320
|
+
req = Rack::MockRequest.new(pool)
|
|
321
|
+
|
|
322
|
+
res = req.get('/')
|
|
323
|
+
res.body.must_equal('{"counter"=>1}')
|
|
324
|
+
cookie = res["Set-Cookie"]
|
|
325
|
+
session_id = cookie[session_match, 1]
|
|
326
|
+
sid = Rack::Session::SessionId.new(session_id)
|
|
327
|
+
|
|
328
|
+
delta_incrementor = lambda do |env|
|
|
329
|
+
# emulate disconjoinment of threading
|
|
330
|
+
env['rack.session'] = env['rack.session'].dup
|
|
331
|
+
Thread.stop
|
|
332
|
+
env['rack.session'][(Time.now.usec*rand).to_i] = true
|
|
333
|
+
incrementor.call(env)
|
|
334
|
+
end
|
|
335
|
+
tses = Rack::Utils::Context.new pool, delta_incrementor
|
|
336
|
+
treq = Rack::MockRequest.new(tses)
|
|
337
|
+
tnum = rand(7).to_i+5
|
|
338
|
+
r = Array.new(tnum) do
|
|
339
|
+
Thread.new(treq) do |run|
|
|
340
|
+
run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
|
|
341
|
+
end
|
|
342
|
+
end.reverse.map{|t| t.run.join.value }
|
|
343
|
+
r.each do |request|
|
|
344
|
+
request['Set-Cookie'].must_equal(cookie)
|
|
345
|
+
request.body.must_include('"counter"=>2')
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
session = pool.with { |c| c.get(sid.private_id) }
|
|
349
|
+
session.size.must_equal(tnum+1) # counter
|
|
350
|
+
session['counter'].must_equal(2) # meeeh
|
|
351
|
+
|
|
352
|
+
tnum = rand(7).to_i+5
|
|
353
|
+
r = Array.new(tnum) do |i|
|
|
354
|
+
app = Rack::Utils::Context.new pool, time_delta
|
|
355
|
+
req = Rack::MockRequest.new app
|
|
356
|
+
Thread.new(req) do |run|
|
|
357
|
+
run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
|
|
358
|
+
end
|
|
359
|
+
end.reverse.map{|t| t.run.join.value }
|
|
360
|
+
r.each do |request|
|
|
361
|
+
request['Set-Cookie'].must_equal(cookie)
|
|
362
|
+
request.body.must_include('"counter"=>3')
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
session = pool.with { |c| c.get(sid.private_id) }
|
|
366
|
+
session.size.must_equal(tnum+1)
|
|
367
|
+
session['counter'].must_equal(3)
|
|
368
|
+
|
|
369
|
+
drop_counter = proc do |env|
|
|
370
|
+
env['rack.session'].delete 'counter'
|
|
371
|
+
env['rack.session']['foo'] = 'bar'
|
|
372
|
+
[200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
|
|
373
|
+
end
|
|
374
|
+
tses = Rack::Utils::Context.new pool, drop_counter
|
|
375
|
+
treq = Rack::MockRequest.new(tses)
|
|
376
|
+
tnum = rand(7).to_i+5
|
|
377
|
+
r = Array.new(tnum) do
|
|
378
|
+
Thread.new(treq) do |run|
|
|
379
|
+
run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
|
|
380
|
+
end
|
|
381
|
+
end.reverse.map{|t| t.run.join.value }
|
|
382
|
+
r.each do |request|
|
|
383
|
+
request['Set-Cookie'].must_equal(cookie)
|
|
384
|
+
request.body.must_include('"foo"=>"bar"')
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
session = pool.with { |c| c.get(sid.private_id) }
|
|
388
|
+
session.size.must_equal(r.size+1)
|
|
389
|
+
session['counter'].must_be_nil
|
|
390
|
+
session['foo'].must_equal('bar')
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
private
|
|
395
|
+
def with_pool_management(*args)
|
|
396
|
+
yield simple(*args)
|
|
397
|
+
yield pooled(*args)
|
|
398
|
+
yield external_pooled(*args)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def simple(app, options = {})
|
|
402
|
+
Rack::Session::Redis.new(app, options)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def pooled(app, options = {})
|
|
406
|
+
Rack::Session::Redis.new(app, options)
|
|
407
|
+
Rack::Session::Redis.new(app, options.merge(pool_size: 5, pool_timeout: 10))
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def external_pooled(app, options = {})
|
|
411
|
+
Rack::Session::Redis.new(app, options.merge(pool: ::ConnectionPool.new(size: 1, timeout: 1) { ::Redis::Store::Factory.create("redis://127.0.0.1:6380/1") }))
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'connection_pool'
|
|
3
|
+
require 'redis/rack/connection'
|
|
4
|
+
|
|
5
|
+
class Redis
|
|
6
|
+
module Rack
|
|
7
|
+
describe Connection do
|
|
8
|
+
def setup
|
|
9
|
+
@defaults = {
|
|
10
|
+
host: 'localhost'
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "can create it's own pool" do
|
|
15
|
+
conn = Connection.new @defaults.merge(pool_size: 5, pool_timeout: 10)
|
|
16
|
+
|
|
17
|
+
conn.pooled?.must_equal true
|
|
18
|
+
conn.pool.class.must_equal ConnectionPool
|
|
19
|
+
conn.pool.instance_variable_get(:@size).must_equal 5
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "can create it's own pool using default Redis server" do
|
|
23
|
+
conn = Connection.new @defaults.merge(pool_size: 5, pool_timeout: 10)
|
|
24
|
+
|
|
25
|
+
conn.pooled?.must_equal true
|
|
26
|
+
|
|
27
|
+
conn.with do |connection|
|
|
28
|
+
connection.to_s.must_match(/127\.0\.0\.1:6379 against DB 0$/)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "can create it's own pool using provided Redis server" do
|
|
33
|
+
conn = Connection.new(redis_server: 'redis://127.0.0.1:6380/1', pool_size: 5, pool_timeout: 10)
|
|
34
|
+
conn.pooled?.must_equal true
|
|
35
|
+
conn.with do |connection|
|
|
36
|
+
connection.to_s.must_match(/127\.0\.0\.1:6380 against DB 1$/)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "can use a supplied pool" do
|
|
41
|
+
pool = ConnectionPool.new size: 1, timeout: 1 do
|
|
42
|
+
::Redis::Store::Factory.create('redis://127.0.0.1:6380/1')
|
|
43
|
+
end
|
|
44
|
+
conn = Connection.new pool: pool
|
|
45
|
+
conn.pooled?.must_equal true
|
|
46
|
+
conn.pool.class.must_equal ConnectionPool
|
|
47
|
+
conn.pool.instance_variable_get(:@size).must_equal 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "uses the specified Redis store when provided" do
|
|
51
|
+
store = ::Redis::Store::Factory.create('redis://127.0.0.1:6380/1')
|
|
52
|
+
conn = Connection.new(redis_store: store)
|
|
53
|
+
|
|
54
|
+
conn.pooled?.must_equal false
|
|
55
|
+
conn.store.to_s.must_match(/127\.0\.0\.1:6380 against DB 1$/)
|
|
56
|
+
conn.store.must_equal(store)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "throws an error when provided Redis store is not the expected type" do
|
|
60
|
+
assert_raises ArgumentError do
|
|
61
|
+
Connection.new(redis_store: ::Redis.new)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "uses the specified Redis server when provided" do
|
|
66
|
+
conn = Connection.new(redis_server: 'redis://127.0.0.1:6380/1')
|
|
67
|
+
|
|
68
|
+
conn.pooled?.must_equal false
|
|
69
|
+
conn.store.to_s.must_match(/127\.0\.0\.1:6380 against DB 1$/)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "does not include nil options for the connection pool" do
|
|
73
|
+
conn = Connection.new
|
|
74
|
+
conn.pool_options.must_be_empty
|
|
75
|
+
|
|
76
|
+
conn = Connection.new(pool_size: nil)
|
|
77
|
+
conn.pool_options.must_be_empty
|
|
78
|
+
|
|
79
|
+
conn = Connection.new(pool_timeout: nil)
|
|
80
|
+
conn.pool_options.must_be_empty
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: redis-rack-my-checksub
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 2.1.3
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Luca Guidi
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-08-04 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: redis-store
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "<"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '1.2'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "<"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '2'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.2'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: rack
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 2.0.8
|
|
40
|
+
- - "<"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '3'
|
|
43
|
+
type: :runtime
|
|
44
|
+
prerelease: false
|
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: 2.0.8
|
|
50
|
+
- - "<"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '3'
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: rake
|
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: 12.3.3
|
|
60
|
+
type: :development
|
|
61
|
+
prerelease: false
|
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: 12.3.3
|
|
67
|
+
- !ruby/object:Gem::Dependency
|
|
68
|
+
name: bundler
|
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '1'
|
|
74
|
+
- - "<"
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '3'
|
|
77
|
+
type: :development
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">"
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '1'
|
|
84
|
+
- - "<"
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '3'
|
|
87
|
+
- !ruby/object:Gem::Dependency
|
|
88
|
+
name: mocha
|
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - "~>"
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: 0.14.0
|
|
94
|
+
type: :development
|
|
95
|
+
prerelease: false
|
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - "~>"
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: 0.14.0
|
|
101
|
+
- !ruby/object:Gem::Dependency
|
|
102
|
+
name: minitest
|
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - "~>"
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '5'
|
|
108
|
+
type: :development
|
|
109
|
+
prerelease: false
|
|
110
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - "~>"
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '5'
|
|
115
|
+
- !ruby/object:Gem::Dependency
|
|
116
|
+
name: connection_pool
|
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - "~>"
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: 1.2.0
|
|
122
|
+
type: :development
|
|
123
|
+
prerelease: false
|
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - "~>"
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: 1.2.0
|
|
129
|
+
description: Redis Store for Rack applications
|
|
130
|
+
email:
|
|
131
|
+
- me@lucaguidi.com
|
|
132
|
+
executables: []
|
|
133
|
+
extensions: []
|
|
134
|
+
extra_rdoc_files: []
|
|
135
|
+
files:
|
|
136
|
+
- ".github/auto-assign-issues.yml"
|
|
137
|
+
- ".gitignore"
|
|
138
|
+
- ".ruby-version"
|
|
139
|
+
- ".travis.yml"
|
|
140
|
+
- CHANGELOG.md
|
|
141
|
+
- CODEOWNERS
|
|
142
|
+
- Gemfile
|
|
143
|
+
- MIT-LICENSE
|
|
144
|
+
- README.md
|
|
145
|
+
- Rakefile
|
|
146
|
+
- bin/appraisal
|
|
147
|
+
- bin/rake
|
|
148
|
+
- lib/rack/session/redis.rb
|
|
149
|
+
- lib/redis-rack.rb
|
|
150
|
+
- lib/redis/rack/connection.rb
|
|
151
|
+
- lib/redis/rack/version.rb
|
|
152
|
+
- redis-rack.gemspec
|
|
153
|
+
- test/rack/session/redis_test.rb
|
|
154
|
+
- test/redis/rack/connection_test.rb
|
|
155
|
+
- test/test_helper.rb
|
|
156
|
+
homepage: http://redis-store.org/redis-rack
|
|
157
|
+
licenses:
|
|
158
|
+
- MIT
|
|
159
|
+
metadata: {}
|
|
160
|
+
post_install_message:
|
|
161
|
+
rdoc_options: []
|
|
162
|
+
require_paths:
|
|
163
|
+
- lib
|
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
|
+
requirements:
|
|
166
|
+
- - ">="
|
|
167
|
+
- !ruby/object:Gem::Version
|
|
168
|
+
version: '0'
|
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
requirements: []
|
|
175
|
+
rubygems_version: 3.1.2
|
|
176
|
+
signing_key:
|
|
177
|
+
specification_version: 4
|
|
178
|
+
summary: Redis Store for Rack
|
|
179
|
+
test_files:
|
|
180
|
+
- test/rack/session/redis_test.rb
|
|
181
|
+
- test/redis/rack/connection_test.rb
|
|
182
|
+
- test/test_helper.rb
|