dalli 2.7.11 → 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/{History.md → CHANGELOG.md} +146 -0
  3. data/Gemfile +3 -9
  4. data/README.md +27 -201
  5. data/lib/dalli/cas/client.rb +1 -57
  6. data/lib/dalli/client.rb +226 -265
  7. data/lib/dalli/compressor.rb +12 -2
  8. data/lib/dalli/key_manager.rb +121 -0
  9. data/lib/dalli/options.rb +6 -7
  10. data/lib/dalli/pipelined_getter.rb +177 -0
  11. data/lib/dalli/protocol/base.rb +241 -0
  12. data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
  13. data/lib/dalli/protocol/binary/response_header.rb +36 -0
  14. data/lib/dalli/protocol/binary/response_processor.rb +239 -0
  15. data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
  16. data/lib/dalli/protocol/binary.rb +173 -0
  17. data/lib/dalli/protocol/connection_manager.rb +252 -0
  18. data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
  19. data/lib/dalli/protocol/meta/request_formatter.rb +121 -0
  20. data/lib/dalli/protocol/meta/response_processor.rb +211 -0
  21. data/lib/dalli/protocol/meta.rb +178 -0
  22. data/lib/dalli/protocol/response_buffer.rb +54 -0
  23. data/lib/dalli/protocol/server_config_parser.rb +86 -0
  24. data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
  25. data/lib/dalli/protocol/value_compressor.rb +85 -0
  26. data/lib/dalli/protocol/value_marshaller.rb +59 -0
  27. data/lib/dalli/protocol/value_serializer.rb +91 -0
  28. data/lib/dalli/protocol.rb +8 -0
  29. data/lib/dalli/ring.rb +94 -83
  30. data/lib/dalli/server.rb +3 -749
  31. data/lib/dalli/servers_arg_normalizer.rb +54 -0
  32. data/lib/dalli/socket.rb +117 -137
  33. data/lib/dalli/version.rb +4 -1
  34. data/lib/dalli.rb +42 -14
  35. data/lib/rack/session/dalli.rb +103 -94
  36. metadata +161 -11
  37. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
  38. data/lib/active_support/cache/dalli_store.rb +0 -441
  39. data/lib/dalli/railtie.rb +0 -8
@@ -1,16 +1,22 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'rack/session/abstract/id'
3
4
  require 'dalli'
5
+ require 'connection_pool'
6
+ require 'English'
4
7
 
5
8
  module Rack
6
9
  module Session
7
- class Dalli < defined?(Abstract::Persisted) ? Abstract::Persisted : Abstract::ID
8
- attr_reader :pool, :mutex
10
+ # Rack::Session::Dalli provides memcached based session management.
11
+ class Dalli < Abstract::PersistedSecure
12
+ attr_reader :data
9
13
 
14
+ # Don't freeze this until we fix the specs/implementation
15
+ # rubocop:disable Style/MutableConstant
10
16
  DEFAULT_DALLI_OPTIONS = {
11
- :namespace => 'rack:session',
12
- :memcache_server => 'localhost:11211'
17
+ namespace: 'rack:session'
13
18
  }
19
+ # rubocop:enable Style/MutableConstant
14
20
 
15
21
  # Brings in a new Rack::Session::Dalli middleware with the given
16
22
  # `:memcache_server`. The server is either a hostname, or a
@@ -26,25 +32,14 @@ module Rack
26
32
  # ENV['MEMCACHE_SERVERS'] and use that value if it is available, or fall
27
33
  # back to the same default behavior described above.
28
34
  #
29
- # Rack::Session::Dalli is intended to be a drop-in replacement for
30
- # Rack::Session::Memcache. It accepts additional options that control the
31
- # behavior of Rack::Session, Dalli::Client, and an optional
32
- # ConnectionPool. First and foremost, if you wish to instantiate your own
33
- # Dalli::Client (or ConnectionPool) and use that instead of letting
34
- # Rack::Session::Dalli instantiate it on your behalf, simply pass it in
35
- # as the `:cache` option. Please note that you will be responsible for
36
- # setting the namespace and any other options on Dalli::Client.
37
- #
38
- # Secondly, if you're not using the `:cache` option, Rack::Session::Dalli
39
- # accepts the same options as Dalli::Client, so it's worth reviewing its
40
- # documentation. Perhaps most importantly, if you don't specify a
41
- # `:namespace` option, Rack::Session::Dalli will default to using
42
- # "rack:session".
35
+ # Rack::Session::Dalli accepts the same options as Dalli::Client, so
36
+ # it's worth reviewing its documentation. Perhaps most importantly,
37
+ # if you don't specify a `:namespace` option, Rack::Session::Dalli
38
+ # will default to using 'rack:session'.
43
39
  #
44
- # Whether you are using the `:cache` option or not, it is not recommend
45
- # to set `:expires_in`. Instead, use `:expire_after`, which will control
46
- # both the expiration of the client cookie as well as the expiration of
47
- # the corresponding entry in memcached.
40
+ # It is not recommended to set `:expires_in`. Instead, use `:expire_after`,
41
+ # which will control both the expiration of the client cookie as well
42
+ # as the expiration of the corresponding entry in memcached.
48
43
  #
49
44
  # Rack::Session::Dalli also accepts a host of options that control how
50
45
  # the sessions and session cookies are managed, including the
@@ -66,116 +61,130 @@ module Rack
66
61
  # for more information about it and its default options (which would only
67
62
  # be applicable if you supplied one of the two options, but not both).
68
63
  #
69
- def initialize(app, options={})
64
+ def initialize(app, options = {})
70
65
  # Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
71
66
  super
72
67
 
73
68
  # Determine the default TTL for newly-created sessions
74
- @default_ttl = ttl @default_options[:expire_after]
75
-
76
- # Normalize and validate passed options
77
- cache, mserv, mopts, popts = extract_dalli_options options
78
-
79
- @pool =
80
- if cache # caller passed a Dalli::Client or ConnectionPool instance
81
- cache
82
- elsif popts # caller passed ConnectionPool options
83
- ConnectionPool.new(popts) { ::Dalli::Client.new(mserv, mopts) }
84
- else
85
- ::Dalli::Client.new(mserv, mopts)
86
- end
69
+ @default_ttl = ttl(@default_options[:expire_after])
70
+ @data = build_data_source(options)
71
+ end
87
72
 
88
- if @pool.respond_to?(:alive!) # is a Dalli::Client
89
- @mutex = Mutex.new
73
+ def find_session(_req, sid)
74
+ with_dalli_client([nil, {}]) do |dc|
75
+ existing_session = existing_session_for_sid(dc, sid)
76
+ return [sid, existing_session] unless existing_session.nil?
90
77
 
91
- @pool.alive!
78
+ [create_sid_with_empty_session(dc), {}]
92
79
  end
93
80
  end
94
81
 
95
- def get_session(env, sid)
96
- with_block(env, [nil, {}]) do |dc|
97
- unless sid and !sid.empty? and session = dc.get(sid)
98
- old_sid, sid, session = sid, generate_sid_with(dc), {}
99
- unless dc.add(sid, session, @default_ttl)
100
- sid = old_sid
101
- redo # generate a new sid and try again
102
- end
103
- end
104
- [sid, session]
105
- end
106
- end
82
+ def write_session(_req, sid, session, options)
83
+ return false unless sid
107
84
 
108
- def set_session(env, session_id, new_session, options)
109
- return false unless session_id
85
+ key = memcached_key_from_sid(sid)
86
+ return false unless key
110
87
 
111
- with_block(env, false) do |dc|
112
- dc.set(session_id, new_session, ttl(options[:expire_after]))
113
- session_id
88
+ with_dalli_client(false) do |dc|
89
+ dc.set(memcached_key_from_sid(sid), session, ttl(options[:expire_after]))
90
+ sid
114
91
  end
115
92
  end
116
93
 
117
- def destroy_session(env, session_id, options)
118
- with_block(env) do |dc|
119
- dc.delete(session_id)
94
+ def delete_session(_req, sid, options)
95
+ with_dalli_client do |dc|
96
+ key = memcached_key_from_sid(sid)
97
+ dc.delete(key) if key
120
98
  generate_sid_with(dc) unless options[:drop]
121
99
  end
122
100
  end
123
101
 
124
- if defined?(Abstract::Persisted)
125
- def find_session(req, sid)
126
- get_session req.env, sid
127
- end
102
+ private
103
+
104
+ def memcached_key_from_sid(sid)
105
+ sid.private_id if sid.respond_to?(:private_id)
106
+ end
107
+
108
+ def existing_session_for_sid(client, sid)
109
+ return nil unless sid && !sid.empty?
110
+
111
+ key = memcached_key_from_sid(sid)
112
+ return nil if key.nil?
113
+
114
+ client.get(key)
115
+ end
128
116
 
129
- def write_session(req, sid, session, options)
130
- set_session req.env, sid, session, options
117
+ def create_sid_with_empty_session(client)
118
+ loop do
119
+ sid = generate_sid_with(client)
120
+ key = memcached_key_from_sid(sid)
121
+
122
+ break sid if key && client.add(key, {}, @default_ttl)
131
123
  end
124
+ end
132
125
 
133
- def delete_session(req, sid, options)
134
- destroy_session req.env, sid, options
126
+ def generate_sid_with(client)
127
+ loop do
128
+ raw_sid = generate_sid
129
+ sid = raw_sid.is_a?(String) ? Rack::Session::SessionId.new(raw_sid) : raw_sid
130
+ key = memcached_key_from_sid(sid)
131
+ break sid unless key && client.get(key)
135
132
  end
136
133
  end
137
134
 
138
- private
135
+ def build_data_source(options)
136
+ server_configurations, client_options, pool_options = extract_dalli_options(options)
139
137
 
140
- def extract_dalli_options(options)
141
- return [options[:cache]] if options[:cache]
138
+ if pool_options.empty?
139
+ ::Dalli::Client.new(server_configurations, client_options)
140
+ else
141
+ ensure_connection_pool_added!
142
+ ConnectionPool.new(pool_options) do
143
+ ::Dalli::Client.new(server_configurations, client_options.merge(threadsafe: false))
144
+ end
145
+ end
146
+ end
142
147
 
143
- # Filter out Rack::Session-specific options and apply our defaults
144
- mopts = DEFAULT_DALLI_OPTIONS.merge \
145
- options.reject {|k, _| DEFAULT_OPTIONS.key? k }
146
- mserv = mopts.delete :memcache_server
148
+ def extract_dalli_options(options)
149
+ raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache]
147
150
 
148
- if mopts[:pool_size] || mopts[:pool_timeout]
149
- popts = {}
150
- popts[:size] = mopts.delete :pool_size if mopts[:pool_size]
151
- popts[:timeout] = mopts.delete :pool_timeout if mopts[:pool_timeout]
151
+ client_options = retrieve_client_options(options)
152
+ server_configurations = client_options.delete(:memcache_server)
152
153
 
153
- # For a connection pool, locking is handled at the pool level
154
- mopts[:threadsafe] = false unless mopts.key? :threadsafe
155
- end
154
+ [server_configurations, client_options, retrieve_pool_options(options)]
155
+ end
156
156
 
157
- [nil, mserv, mopts, popts]
157
+ def retrieve_client_options(options)
158
+ # Filter out Rack::Session-specific options and apply our defaults
159
+ filtered_opts = options.reject { |k, _| DEFAULT_OPTIONS.key? k }
160
+ DEFAULT_DALLI_OPTIONS.merge(filtered_opts)
158
161
  end
159
162
 
160
- def generate_sid_with(dc)
161
- while true
162
- sid = generate_sid
163
- break sid unless dc.get(sid)
163
+ def retrieve_pool_options(options)
164
+ {}.tap do |pool_options|
165
+ pool_options[:size] = options.delete(:pool_size) if options[:pool_size]
166
+ pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout]
164
167
  end
165
168
  end
166
169
 
167
- def with_block(env, default=nil, &block)
168
- @mutex.lock if @mutex and env['rack.multithread']
169
- @pool.with(&block)
170
+ def ensure_connection_pool_added!
171
+ require 'connection_pool'
172
+ rescue LoadError => e
173
+ warn "You don't have connection_pool installed in your application. " \
174
+ 'Please add it to your Gemfile and run bundle install'
175
+ raise e
176
+ end
177
+
178
+ def with_dalli_client(result_on_error = nil, &block)
179
+ @data.with(&block)
170
180
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
171
- raise if $!.message =~ /undefined class/
181
+ raise if /undefined class/.match?($ERROR_INFO.message)
182
+
172
183
  if $VERBOSE
173
184
  warn "#{self} is unable to find memcached server."
174
- warn $!.inspect
185
+ warn $ERROR_INFO.inspect
175
186
  end
176
- default
177
- ensure
178
- @mutex.unlock if @mutex and @mutex.locked?
187
+ result_on_error
179
188
  end
180
189
 
181
190
  def ttl(expire_after)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dalli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.11
4
+ version: 3.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
@@ -9,8 +9,140 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-09-26 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2022-10-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: connection_pool
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: minitest
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '5'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '5'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rack
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2.0'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 2.2.0
52
+ type: :development
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '2.0'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 2.2.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13.0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13.0'
76
+ - !ruby/object:Gem::Dependency
77
+ name: rubocop
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: rubocop-minitest
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ - !ruby/object:Gem::Dependency
105
+ name: rubocop-performance
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ type: :development
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ - !ruby/object:Gem::Dependency
119
+ name: rubocop-rake
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ - !ruby/object:Gem::Dependency
133
+ name: simplecov
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ type: :development
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
14
146
  description: High performance memcached client for Ruby
15
147
  email:
16
148
  - peter.m.goldstein@gmail.com
@@ -19,27 +151,46 @@ executables: []
19
151
  extensions: []
20
152
  extra_rdoc_files: []
21
153
  files:
154
+ - CHANGELOG.md
22
155
  - Gemfile
23
- - History.md
24
156
  - LICENSE
25
157
  - README.md
26
- - lib/action_dispatch/middleware/session/dalli_store.rb
27
- - lib/active_support/cache/dalli_store.rb
28
158
  - lib/dalli.rb
29
159
  - lib/dalli/cas/client.rb
30
160
  - lib/dalli/client.rb
31
161
  - lib/dalli/compressor.rb
162
+ - lib/dalli/key_manager.rb
32
163
  - lib/dalli/options.rb
33
- - lib/dalli/railtie.rb
164
+ - lib/dalli/pipelined_getter.rb
165
+ - lib/dalli/protocol.rb
166
+ - lib/dalli/protocol/base.rb
167
+ - lib/dalli/protocol/binary.rb
168
+ - lib/dalli/protocol/binary/request_formatter.rb
169
+ - lib/dalli/protocol/binary/response_header.rb
170
+ - lib/dalli/protocol/binary/response_processor.rb
171
+ - lib/dalli/protocol/binary/sasl_authentication.rb
172
+ - lib/dalli/protocol/connection_manager.rb
173
+ - lib/dalli/protocol/meta.rb
174
+ - lib/dalli/protocol/meta/key_regularizer.rb
175
+ - lib/dalli/protocol/meta/request_formatter.rb
176
+ - lib/dalli/protocol/meta/response_processor.rb
177
+ - lib/dalli/protocol/response_buffer.rb
178
+ - lib/dalli/protocol/server_config_parser.rb
179
+ - lib/dalli/protocol/ttl_sanitizer.rb
180
+ - lib/dalli/protocol/value_compressor.rb
181
+ - lib/dalli/protocol/value_marshaller.rb
182
+ - lib/dalli/protocol/value_serializer.rb
34
183
  - lib/dalli/ring.rb
35
184
  - lib/dalli/server.rb
185
+ - lib/dalli/servers_arg_normalizer.rb
36
186
  - lib/dalli/socket.rb
37
187
  - lib/dalli/version.rb
38
188
  - lib/rack/session/dalli.rb
39
189
  homepage: https://github.com/petergoldstein/dalli
40
190
  licenses:
41
191
  - MIT
42
- metadata: {}
192
+ metadata:
193
+ rubygems_mfa_required: 'true'
43
194
  post_install_message:
44
195
  rdoc_options: []
45
196
  require_paths:
@@ -48,15 +199,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
48
199
  requirements:
49
200
  - - ">="
50
201
  - !ruby/object:Gem::Version
51
- version: '0'
202
+ version: '2.6'
52
203
  required_rubygems_version: !ruby/object:Gem::Requirement
53
204
  requirements:
54
205
  - - ">="
55
206
  - !ruby/object:Gem::Version
56
207
  version: '0'
57
208
  requirements: []
58
- rubyforge_project:
59
- rubygems_version: 2.7.6.2
209
+ rubygems_version: 3.3.24
60
210
  signing_key:
61
211
  specification_version: 4
62
212
  summary: High performance memcached client for Ruby
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'active_support/cache'
3
- require 'action_dispatch/middleware/session/abstract_store'
4
- require 'dalli'
5
-
6
- # Dalli-based session store for Rails 3.0.
7
- module ActionDispatch
8
- module Session
9
- class DalliStore < AbstractStore
10
- def initialize(app, options = {})
11
- # Support old :expires option
12
- options[:expire_after] ||= options[:expires]
13
-
14
- super
15
-
16
- @default_options = { :namespace => 'rack:session' }.merge(@default_options)
17
-
18
- @pool = options[:cache] || begin
19
- Dalli::Client.new(
20
- @default_options[:memcache_server], @default_options)
21
- end
22
- @namespace = @default_options[:namespace]
23
-
24
- @raise_errors = !!@default_options[:raise_errors]
25
-
26
- super
27
- end
28
-
29
- def reset
30
- @pool.reset
31
- end
32
-
33
- private
34
-
35
- def get_session(env, sid)
36
- sid = generate_sid unless sid and !sid.empty?
37
- begin
38
- session = @pool.get(sid) || {}
39
- rescue Dalli::DalliError => ex
40
- # re-raise ArgumentError so Rails' session abstract_store.rb can autoload any missing models
41
- raise ArgumentError, ex.message if ex.message =~ /unmarshal/
42
- Rails.logger.warn("Session::DalliStore#get: #{ex.message}")
43
- session = {}
44
- end
45
- [sid, session]
46
- end
47
-
48
- def set_session(env, sid, session_data, options = nil)
49
- options ||= env[ENV_SESSION_OPTIONS_KEY]
50
- expiry = options[:expire_after]
51
- @pool.set(sid, session_data, expiry)
52
- sid
53
- rescue Dalli::DalliError
54
- Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
55
- raise if @raise_errors
56
- false
57
- end
58
-
59
- def destroy_session(env, session_id, options)
60
- begin
61
- @pool.delete(session_id)
62
- rescue Dalli::DalliError
63
- Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
64
- raise if @raise_errors
65
- end
66
- return nil if options[:drop]
67
- generate_sid
68
- end
69
-
70
- def destroy(env)
71
- if sid = current_session_id(env)
72
- @pool.delete(sid)
73
- end
74
- rescue Dalli::DalliError
75
- Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
76
- raise if @raise_errors
77
- false
78
- end
79
-
80
- end
81
- end
82
- end