dalli 2.7.11 → 3.2.0

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 +7 -6
  3. data/History.md +124 -0
  4. data/README.md +26 -200
  5. data/lib/dalli/cas/client.rb +1 -57
  6. data/lib/dalli/client.rb +230 -263
  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 +90 -81
  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 +95 -95
  36. metadata +118 -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,121 @@ 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
87
-
88
- if @pool.respond_to?(:alive!) # is a Dalli::Client
89
- @mutex = Mutex.new
90
-
91
- @pool.alive!
92
- end
69
+ @default_ttl = ttl(@default_options[:expire_after])
70
+ @data = build_data_source(options)
93
71
  end
94
72
 
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]
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), {}]
105
79
  end
106
80
  end
107
81
 
108
- def set_session(env, session_id, new_session, options)
109
- return false unless session_id
82
+ def write_session(_req, sid, session, options)
83
+ return false unless sid
110
84
 
111
- with_block(env, false) do |dc|
112
- dc.set(session_id, new_session, ttl(options[:expire_after]))
113
- session_id
85
+ with_dalli_client(false) do |dc|
86
+ dc.set(memcached_key_from_sid(sid), session, ttl(options[:expire_after]))
87
+ sid
114
88
  end
115
89
  end
116
90
 
117
- def destroy_session(env, session_id, options)
118
- with_block(env) do |dc|
119
- dc.delete(session_id)
91
+ def delete_session(_req, sid, options)
92
+ with_dalli_client do |dc|
93
+ dc.delete(memcached_key_from_sid(sid))
120
94
  generate_sid_with(dc) unless options[:drop]
121
95
  end
122
96
  end
123
97
 
124
- if defined?(Abstract::Persisted)
125
- def find_session(req, sid)
126
- get_session req.env, sid
127
- end
98
+ private
99
+
100
+ def memcached_key_from_sid(sid)
101
+ sid.private_id
102
+ end
103
+
104
+ def existing_session_for_sid(client, sid)
105
+ return nil unless sid && !sid.empty?
106
+
107
+ client.get(memcached_key_from_sid(sid))
108
+ end
128
109
 
129
- def write_session(req, sid, session, options)
130
- set_session req.env, sid, session, options
110
+ def create_sid_with_empty_session(client)
111
+ loop do
112
+ sid = generate_sid_with(client)
113
+
114
+ break sid if client.add(memcached_key_from_sid(sid), {}, @default_ttl)
131
115
  end
116
+ end
132
117
 
133
- def delete_session(req, sid, options)
134
- destroy_session req.env, sid, options
118
+ def generate_sid_with(client)
119
+ loop do
120
+ raw_sid = generate_sid
121
+ sid = raw_sid.is_a?(String) ? Rack::Session::SessionId.new(raw_sid) : raw_sid
122
+ break sid unless client.get(memcached_key_from_sid(sid))
135
123
  end
136
124
  end
137
125
 
138
- private
126
+ def build_data_source(options)
127
+ server_configurations, client_options, pool_options = extract_dalli_options(options)
139
128
 
140
- def extract_dalli_options(options)
141
- return [options[:cache]] if options[:cache]
129
+ if pool_options.empty?
130
+ ::Dalli::Client.new(server_configurations, client_options)
131
+ else
132
+ ensure_connection_pool_added!
133
+ ConnectionPool.new(pool_options) do
134
+ ::Dalli::Client.new(server_configurations, client_options.merge(threadsafe: false))
135
+ end
136
+ end
137
+ end
142
138
 
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
139
+ def extract_dalli_options(options)
140
+ raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache]
147
141
 
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]
142
+ client_options = retrieve_client_options(options)
143
+ server_configurations = client_options.delete(:memcache_server)
152
144
 
153
- # For a connection pool, locking is handled at the pool level
154
- mopts[:threadsafe] = false unless mopts.key? :threadsafe
155
- end
145
+ [server_configurations, client_options, retrieve_pool_options(options)]
146
+ end
156
147
 
157
- [nil, mserv, mopts, popts]
148
+ def retrieve_client_options(options)
149
+ # Filter out Rack::Session-specific options and apply our defaults
150
+ filtered_opts = options.reject { |k, _| DEFAULT_OPTIONS.key? k }
151
+ DEFAULT_DALLI_OPTIONS.merge(filtered_opts)
158
152
  end
159
153
 
160
- def generate_sid_with(dc)
161
- while true
162
- sid = generate_sid
163
- break sid unless dc.get(sid)
154
+ def retrieve_pool_options(options)
155
+ {}.tap do |pool_options|
156
+ pool_options[:size] = options.delete(:pool_size) if options[:pool_size]
157
+ pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout]
164
158
  end
165
159
  end
166
160
 
167
- def with_block(env, default=nil, &block)
168
- @mutex.lock if @mutex and env['rack.multithread']
169
- @pool.with(&block)
161
+ def ensure_connection_pool_added!
162
+ require 'connection_pool'
163
+ rescue LoadError => e
164
+ warn "You don't have connection_pool installed in your application. "\
165
+ 'Please add it to your Gemfile and run bundle install'
166
+ raise e
167
+ end
168
+
169
+ def with_dalli_client(result_on_error = nil, &block)
170
+ @data.with(&block)
170
171
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
171
- raise if $!.message =~ /undefined class/
172
+ raise if /undefined class/.match?($ERROR_INFO.message)
173
+
172
174
  if $VERBOSE
173
175
  warn "#{self} is unable to find memcached server."
174
- warn $!.inspect
176
+ warn $ERROR_INFO.inspect
175
177
  end
176
- default
177
- ensure
178
- @mutex.unlock if @mutex and @mutex.locked?
178
+ result_on_error
179
179
  end
180
180
 
181
181
  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.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
@@ -9,8 +9,98 @@ 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-01-03 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: rack
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '2.0'
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 2.2.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - "~>"
43
+ - !ruby/object:Gem::Version
44
+ version: '2.0'
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.2.0
48
+ - !ruby/object:Gem::Dependency
49
+ name: rubocop
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rubocop-minitest
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ - !ruby/object:Gem::Dependency
77
+ name: rubocop-performance
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-rake
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'
14
104
  description: High performance memcached client for Ruby
15
105
  email:
16
106
  - peter.m.goldstein@gmail.com
@@ -23,23 +113,42 @@ files:
23
113
  - History.md
24
114
  - LICENSE
25
115
  - README.md
26
- - lib/action_dispatch/middleware/session/dalli_store.rb
27
- - lib/active_support/cache/dalli_store.rb
28
116
  - lib/dalli.rb
29
117
  - lib/dalli/cas/client.rb
30
118
  - lib/dalli/client.rb
31
119
  - lib/dalli/compressor.rb
120
+ - lib/dalli/key_manager.rb
32
121
  - lib/dalli/options.rb
33
- - lib/dalli/railtie.rb
122
+ - lib/dalli/pipelined_getter.rb
123
+ - lib/dalli/protocol.rb
124
+ - lib/dalli/protocol/base.rb
125
+ - lib/dalli/protocol/binary.rb
126
+ - lib/dalli/protocol/binary/request_formatter.rb
127
+ - lib/dalli/protocol/binary/response_header.rb
128
+ - lib/dalli/protocol/binary/response_processor.rb
129
+ - lib/dalli/protocol/binary/sasl_authentication.rb
130
+ - lib/dalli/protocol/connection_manager.rb
131
+ - lib/dalli/protocol/meta.rb
132
+ - lib/dalli/protocol/meta/key_regularizer.rb
133
+ - lib/dalli/protocol/meta/request_formatter.rb
134
+ - lib/dalli/protocol/meta/response_processor.rb
135
+ - lib/dalli/protocol/response_buffer.rb
136
+ - lib/dalli/protocol/server_config_parser.rb
137
+ - lib/dalli/protocol/ttl_sanitizer.rb
138
+ - lib/dalli/protocol/value_compressor.rb
139
+ - lib/dalli/protocol/value_marshaller.rb
140
+ - lib/dalli/protocol/value_serializer.rb
34
141
  - lib/dalli/ring.rb
35
142
  - lib/dalli/server.rb
143
+ - lib/dalli/servers_arg_normalizer.rb
36
144
  - lib/dalli/socket.rb
37
145
  - lib/dalli/version.rb
38
146
  - lib/rack/session/dalli.rb
39
147
  homepage: https://github.com/petergoldstein/dalli
40
148
  licenses:
41
149
  - MIT
42
- metadata: {}
150
+ metadata:
151
+ rubygems_mfa_required: 'true'
43
152
  post_install_message:
44
153
  rdoc_options: []
45
154
  require_paths:
@@ -48,15 +157,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
48
157
  requirements:
49
158
  - - ">="
50
159
  - !ruby/object:Gem::Version
51
- version: '0'
160
+ version: '2.5'
52
161
  required_rubygems_version: !ruby/object:Gem::Requirement
53
162
  requirements:
54
163
  - - ">="
55
164
  - !ruby/object:Gem::Version
56
165
  version: '0'
57
166
  requirements: []
58
- rubyforge_project:
59
- rubygems_version: 2.7.6.2
167
+ rubygems_version: 3.3.4
60
168
  signing_key:
61
169
  specification_version: 4
62
170
  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