dalli 2.7.6 → 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 +5 -5
  2. data/Gemfile +5 -1
  3. data/History.md +175 -0
  4. data/README.md +27 -213
  5. data/lib/dalli/cas/client.rb +2 -57
  6. data/lib/dalli/client.rb +228 -254
  7. data/lib/dalli/compressor.rb +13 -2
  8. data/lib/dalli/key_manager.rb +113 -0
  9. data/lib/dalli/options.rb +7 -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 +95 -84
  30. data/lib/dalli/server.rb +4 -743
  31. data/lib/dalli/servers_arg_normalizer.rb +54 -0
  32. data/lib/dalli/socket.rb +118 -130
  33. data/lib/dalli/version.rb +5 -1
  34. data/lib/dalli.rb +45 -14
  35. data/lib/rack/session/dalli.rb +156 -50
  36. metadata +64 -27
  37. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -81
  38. data/lib/active_support/cache/dalli_store.rb +0 -403
  39. data/lib/dalli/railtie.rb +0 -7
@@ -1,89 +1,195 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/session/abstract/id'
2
4
  require 'dalli'
5
+ require 'connection_pool'
6
+ require 'English'
3
7
 
4
8
  module Rack
5
9
  module Session
6
- class Dalli < defined?(Abstract::Persisted) ? Abstract::Persisted : Abstract::ID
7
- attr_reader :pool, :mutex
10
+ # Rack::Session::Dalli provides memcached based session management.
11
+ class Dalli < Abstract::PersistedSecure
12
+ attr_reader :data
8
13
 
9
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
10
- :namespace => 'rack:session',
11
- :memcache_server => 'localhost:11211'
14
+ # Don't freeze this until we fix the specs/implementation
15
+ # rubocop:disable Style/MutableConstant
16
+ DEFAULT_DALLI_OPTIONS = {
17
+ namespace: 'rack:session'
18
+ }
19
+ # rubocop:enable Style/MutableConstant
12
20
 
13
- def initialize(app, options={})
21
+ # Brings in a new Rack::Session::Dalli middleware with the given
22
+ # `:memcache_server`. The server is either a hostname, or a
23
+ # host-with-port string in the form of "host_name:port", or an array of
24
+ # such strings. For example:
25
+ #
26
+ # use Rack::Session::Dalli,
27
+ # :memcache_server => "mc.example.com:1234"
28
+ #
29
+ # If no `:memcache_server` option is specified, Rack::Session::Dalli will
30
+ # connect to localhost, port 11211 (the default memcached port). If
31
+ # `:memcache_server` is set to nil, Dalli::Client will look for
32
+ # ENV['MEMCACHE_SERVERS'] and use that value if it is available, or fall
33
+ # back to the same default behavior described above.
34
+ #
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'.
39
+ #
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.
43
+ #
44
+ # Rack::Session::Dalli also accepts a host of options that control how
45
+ # the sessions and session cookies are managed, including the
46
+ # aforementioned `:expire_after` option. Please see the documentation for
47
+ # Rack::Session::Abstract::Persisted for a detailed explanation of these
48
+ # options and their default values.
49
+ #
50
+ # Finally, if your web application is multithreaded, the
51
+ # Rack::Session::Dalli middleware can become a source of contention. You
52
+ # can use a connection pool of Dalli clients by passing in the
53
+ # `:pool_size` and/or `:pool_timeout` options. For example:
54
+ #
55
+ # use Rack::Session::Dalli,
56
+ # :memcache_server => "mc.example.com:1234",
57
+ # :pool_size => 10
58
+ #
59
+ # You must include the `connection_pool` gem in your project if you wish
60
+ # to use pool support. Please see the documentation for ConnectionPool
61
+ # for more information about it and its default options (which would only
62
+ # be applicable if you supplied one of the two options, but not both).
63
+ #
64
+ def initialize(app, options = {})
65
+ # Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
14
66
  super
15
- @mutex = Mutex.new
16
- mserv = @default_options[:memcache_server]
17
- mopts = @default_options.reject{|k,v| !DEFAULT_OPTIONS.include? k }
18
- @pool = options[:cache] || ::Dalli::Client.new(mserv, mopts)
19
- @pool.alive!
67
+
68
+ # Determine the default TTL for newly-created sessions
69
+ @default_ttl = ttl(@default_options[:expire_after])
70
+ @data = build_data_source(options)
20
71
  end
21
72
 
22
- if defined?(Abstract::Persisted)
23
- def find_session(req, sid)
24
- get_session req.env, sid
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?
77
+
78
+ [create_sid_with_empty_session(dc), {}]
25
79
  end
80
+ end
26
81
 
27
- def write_session(req, sid, session, options)
28
- set_session req.env, sid, session, options
82
+ def write_session(_req, sid, session, options)
83
+ return false unless sid
84
+
85
+ key = memcached_key_from_sid(sid)
86
+ return false unless key
87
+
88
+ with_dalli_client(false) do |dc|
89
+ dc.set(memcached_key_from_sid(sid), session, ttl(options[:expire_after]))
90
+ sid
29
91
  end
92
+ end
30
93
 
31
- def delete_session(req, sid, options)
32
- destroy_session req.env, sid, options
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
98
+ generate_sid_with(dc) unless options[:drop]
33
99
  end
34
100
  end
35
101
 
36
- def generate_sid
37
- while true
38
- sid = super
39
- break sid unless @pool.get(sid)
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
116
+
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)
40
123
  end
41
124
  end
42
125
 
43
- def get_session(env, sid)
44
- with_lock(env, [nil, {}]) do
45
- unless sid and !sid.empty? and session = @pool.get(sid)
46
- sid, session = generate_sid, {}
47
- unless @pool.add(sid, session)
48
- raise "Session collision on '#{sid.inspect}'"
49
- end
50
- end
51
- [sid, session]
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)
52
132
  end
53
133
  end
54
134
 
55
- def set_session(env, session_id, new_session, options)
56
- return false unless session_id
57
- expiry = options[:expire_after]
58
- expiry = expiry.nil? ? 0 : expiry + 1
135
+ def build_data_source(options)
136
+ server_configurations, client_options, pool_options = extract_dalli_options(options)
59
137
 
60
- with_lock(env, false) do
61
- @pool.set session_id, new_session, expiry
62
- session_id
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
63
145
  end
64
146
  end
65
147
 
66
- def destroy_session(env, session_id, options)
67
- with_lock(env) do
68
- @pool.delete(session_id)
69
- generate_sid unless options[:drop]
148
+ def extract_dalli_options(options)
149
+ raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache]
150
+
151
+ client_options = retrieve_client_options(options)
152
+ server_configurations = client_options.delete(:memcache_server)
153
+
154
+ [server_configurations, client_options, retrieve_pool_options(options)]
155
+ end
156
+
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)
161
+ end
162
+
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]
70
167
  end
71
168
  end
72
169
 
73
- def with_lock(env, default=nil)
74
- @mutex.lock if env['rack.multithread']
75
- yield
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)
76
180
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
77
- raise if $!.message =~ /undefined class/
181
+ raise if /undefined class/.match?($ERROR_INFO.message)
182
+
78
183
  if $VERBOSE
79
184
  warn "#{self} is unable to find memcached server."
80
- warn $!.inspect
185
+ warn $ERROR_INFO.inspect
81
186
  end
82
- default
83
- ensure
84
- @mutex.unlock if @mutex.locked?
187
+ result_on_error
85
188
  end
86
189
 
190
+ def ttl(expire_after)
191
+ expire_after.nil? ? 0 : expire_after + 1
192
+ end
87
193
  end
88
194
  end
89
195
  end
metadata CHANGED
@@ -1,60 +1,80 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dalli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.6
4
+ version: 3.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
8
8
  - Mike Perham
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-14 00:00:00.000000000 Z
12
+ date: 2022-06-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: minitest
15
+ name: connection_pool
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: 4.2.0
20
+ version: '0'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: 4.2.0
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'
28
42
  - !ruby/object:Gem::Dependency
29
- name: mocha
43
+ name: rack
30
44
  requirement: !ruby/object:Gem::Requirement
31
45
  requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2.0'
32
49
  - - ">="
33
50
  - !ruby/object:Gem::Version
34
- version: '0'
51
+ version: 2.2.0
35
52
  type: :development
36
53
  prerelease: false
37
54
  version_requirements: !ruby/object:Gem::Requirement
38
55
  requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '2.0'
39
59
  - - ">="
40
60
  - !ruby/object:Gem::Version
41
- version: '0'
61
+ version: 2.2.0
42
62
  - !ruby/object:Gem::Dependency
43
- name: rails
63
+ name: rake
44
64
  requirement: !ruby/object:Gem::Requirement
45
65
  requirements:
46
66
  - - "~>"
47
67
  - !ruby/object:Gem::Version
48
- version: '4'
68
+ version: '13.0'
49
69
  type: :development
50
70
  prerelease: false
51
71
  version_requirements: !ruby/object:Gem::Requirement
52
72
  requirements:
53
73
  - - "~>"
54
74
  - !ruby/object:Gem::Version
55
- version: '4'
75
+ version: '13.0'
56
76
  - !ruby/object:Gem::Dependency
57
- name: rake
77
+ name: rubocop
58
78
  requirement: !ruby/object:Gem::Requirement
59
79
  requirements:
60
80
  - - ">="
@@ -68,7 +88,7 @@ dependencies:
68
88
  - !ruby/object:Gem::Version
69
89
  version: '0'
70
90
  - !ruby/object:Gem::Dependency
71
- name: appraisal
91
+ name: rubocop-minitest
72
92
  requirement: !ruby/object:Gem::Requirement
73
93
  requirements:
74
94
  - - ">="
@@ -82,7 +102,7 @@ dependencies:
82
102
  - !ruby/object:Gem::Version
83
103
  version: '0'
84
104
  - !ruby/object:Gem::Dependency
85
- name: connection_pool
105
+ name: rubocop-performance
86
106
  requirement: !ruby/object:Gem::Requirement
87
107
  requirements:
88
108
  - - ">="
@@ -96,7 +116,7 @@ dependencies:
96
116
  - !ruby/object:Gem::Version
97
117
  version: '0'
98
118
  - !ruby/object:Gem::Dependency
99
- name: rdoc
119
+ name: rubocop-rake
100
120
  requirement: !ruby/object:Gem::Requirement
101
121
  requirements:
102
122
  - - ">="
@@ -135,42 +155,59 @@ files:
135
155
  - History.md
136
156
  - LICENSE
137
157
  - README.md
138
- - lib/action_dispatch/middleware/session/dalli_store.rb
139
- - lib/active_support/cache/dalli_store.rb
140
158
  - lib/dalli.rb
141
159
  - lib/dalli/cas/client.rb
142
160
  - lib/dalli/client.rb
143
161
  - lib/dalli/compressor.rb
162
+ - lib/dalli/key_manager.rb
144
163
  - lib/dalli/options.rb
145
- - 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
146
183
  - lib/dalli/ring.rb
147
184
  - lib/dalli/server.rb
185
+ - lib/dalli/servers_arg_normalizer.rb
148
186
  - lib/dalli/socket.rb
149
187
  - lib/dalli/version.rb
150
188
  - lib/rack/session/dalli.rb
151
189
  homepage: https://github.com/petergoldstein/dalli
152
190
  licenses:
153
191
  - MIT
154
- metadata: {}
155
- post_install_message:
156
- rdoc_options:
157
- - "--charset=UTF-8"
192
+ metadata:
193
+ rubygems_mfa_required: 'true'
194
+ post_install_message:
195
+ rdoc_options: []
158
196
  require_paths:
159
197
  - lib
160
198
  required_ruby_version: !ruby/object:Gem::Requirement
161
199
  requirements:
162
200
  - - ">="
163
201
  - !ruby/object:Gem::Version
164
- version: '0'
202
+ version: '2.6'
165
203
  required_rubygems_version: !ruby/object:Gem::Requirement
166
204
  requirements:
167
205
  - - ">="
168
206
  - !ruby/object:Gem::Version
169
207
  version: '0'
170
208
  requirements: []
171
- rubyforge_project:
172
- rubygems_version: 2.4.5.1
173
- signing_key:
209
+ rubygems_version: 3.3.16
210
+ signing_key:
174
211
  specification_version: 4
175
212
  summary: High performance memcached client for Ruby
176
213
  test_files: []
@@ -1,81 +0,0 @@
1
- require 'active_support/cache'
2
- require 'action_dispatch/middleware/session/abstract_store'
3
- require 'dalli'
4
-
5
- # Dalli-based session store for Rails 3.0.
6
- module ActionDispatch
7
- module Session
8
- class DalliStore < AbstractStore
9
- def initialize(app, options = {})
10
- # Support old :expires option
11
- options[:expire_after] ||= options[:expires]
12
-
13
- super
14
-
15
- @default_options = { :namespace => 'rack:session' }.merge(@default_options)
16
-
17
- @pool = options[:cache] || begin
18
- Dalli::Client.new(
19
- @default_options[:memcache_server], @default_options)
20
- end
21
- @namespace = @default_options[:namespace]
22
-
23
- @raise_errors = !!@default_options[:raise_errors]
24
-
25
- super
26
- end
27
-
28
- def reset
29
- @pool.reset
30
- end
31
-
32
- private
33
-
34
- def get_session(env, sid)
35
- sid = generate_sid unless sid and !sid.empty?
36
- begin
37
- session = @pool.get(sid) || {}
38
- rescue Dalli::DalliError => ex
39
- # re-raise ArgumentError so Rails' session abstract_store.rb can autoload any missing models
40
- raise ArgumentError, ex.message if ex.message =~ /unmarshal/
41
- Rails.logger.warn("Session::DalliStore#get: #{ex.message}")
42
- session = {}
43
- end
44
- [sid, session]
45
- end
46
-
47
- def set_session(env, sid, session_data, options = nil)
48
- options ||= env[ENV_SESSION_OPTIONS_KEY]
49
- expiry = options[:expire_after]
50
- @pool.set(sid, session_data, expiry)
51
- sid
52
- rescue Dalli::DalliError
53
- Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
54
- raise if @raise_errors
55
- false
56
- end
57
-
58
- def destroy_session(env, session_id, options)
59
- begin
60
- @pool.delete(session_id)
61
- rescue Dalli::DalliError
62
- Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
63
- raise if @raise_errors
64
- end
65
- return nil if options[:drop]
66
- generate_sid
67
- end
68
-
69
- def destroy(env)
70
- if sid = current_session_id(env)
71
- @pool.delete(sid)
72
- end
73
- rescue Dalli::DalliError
74
- Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
75
- raise if @raise_errors
76
- false
77
- end
78
-
79
- end
80
- end
81
- end