dalli 2.7.8 → 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 +5 -1
  3. data/History.md +159 -0
  4. data/README.md +27 -223
  5. data/lib/dalli/cas/client.rb +1 -57
  6. data/lib/dalli/client.rb +227 -254
  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 -746
  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 +43 -15
  35. data/lib/rack/session/dalli.rb +103 -94
  36. metadata +64 -27
  37. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
  38. data/lib/active_support/cache/dalli_store.rb +0 -429
  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,60 +1,80 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dalli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.8
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: 2018-04-11 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.7.5
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,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