dalli 2.7.11 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of dalli might be problematic. Click here for more details.

Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -9
  3. data/History.md +137 -0
  4. data/README.md +26 -200
  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 +113 -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 +108 -0
  20. data/lib/dalli/protocol/meta/response_processor.rb +211 -0
  21. data/lib/dalli/protocol/meta.rb +177 -0
  22. data/lib/dalli/protocol/response_buffer.rb +54 -0
  23. data/lib/dalli/protocol/server_config_parser.rb +84 -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 +160 -10
  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.2
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-06-15 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
@@ -23,23 +155,42 @@ files:
23
155
  - 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.16
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