dalli 5.2.7 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'uri'
4
+
3
5
  module Dalli
4
6
  ##
5
7
  # This module contains methods for validating and normalizing the servers
@@ -26,6 +28,8 @@ module Dalli
26
28
  module ServersArgNormalizer
27
29
  ENV_VAR_NAME = 'MEMCACHE_SERVERS'
28
30
  DEFAULT_SERVERS = ['127.0.0.1:11211'].freeze
31
+ URI_CREDENTIAL_WARNING = 'Dalli 5.0 removed SASL authentication. ' \
32
+ 'Credentials in memcached:// URIs are ignored.'
29
33
 
30
34
  ##
31
35
  # Normalizes the argument into an array of servers.
@@ -34,7 +38,21 @@ module Dalli
34
38
  def self.normalize_servers(arg)
35
39
  arg = apply_defaults(arg)
36
40
  validate_type(arg)
37
- Array(arg).flat_map { |s| s.split(',') }.reject(&:empty?)
41
+ Array(arg).flat_map { |s| s.split(',') }.reject(&:empty?).map { |s| without_credentials(s) }
42
+ end
43
+
44
+ # Dalli 5 doesn't use credentials in a memcached:// URI, so they are
45
+ # dropped here, with a warning, rather than kept in the client's server
46
+ # list where #inspect would show them.
47
+ def self.without_credentials(server)
48
+ return server unless server.start_with?('memcached://') && server.include?('@')
49
+
50
+ uri = URI.parse(server)
51
+ return server unless uri.userinfo
52
+
53
+ Dalli.logger.warn(URI_CREDENTIAL_WARNING)
54
+ uri.user = nil # clears the password too
55
+ uri.to_s
38
56
  end
39
57
 
40
58
  def self.apply_defaults(arg)
data/lib/dalli/socket.rb CHANGED
@@ -192,6 +192,9 @@ module Dalli
192
192
  end
193
193
 
194
194
  def self.init_socket_options(sock, options)
195
+ # CRuby sets this on every socket; TruffleRuby doesn't, so a process
196
+ # the app spawns would inherit the connection and hold it open
197
+ sock.close_on_exec = true
195
198
  configure_tcp_options(sock, options)
196
199
  configure_socket_buffers(sock, options)
197
200
  configure_timeout(sock, options)
@@ -317,6 +320,8 @@ module Dalli
317
320
  end
318
321
 
319
322
  def self.init_socket_options(sock, options)
323
+ # Not inherited by spawned processes; see TCP.init_socket_options
324
+ sock.close_on_exec = true
320
325
  # https://man7.org/linux/man-pages/man7/unix.7.html
321
326
  sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
322
327
  sock.timeout = options[:socket_timeout] if options[:socket_timeout] && sock.respond_to?(:timeout=)
data/lib/dalli/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dalli
4
- VERSION = '5.2.7'
4
+ VERSION = '5.3.0'
5
5
 
6
6
  MIN_SUPPORTED_MEMCACHED_VERSION = '1.6.27'
7
7
  end
data/lib/dalli.rb CHANGED
@@ -29,7 +29,19 @@ module Dalli
29
29
  class ServerError < DalliError; end
30
30
 
31
31
  # socket/server communication error that can be retried
32
- class RetryableNetworkError < NetworkError; end
32
+ class RetryableNetworkError < NetworkError
33
+ def initialize(message = nil, request_sent: false)
34
+ super(message)
35
+ @request_sent = request_sent
36
+ end
37
+
38
+ # True when some of the failed request was handed to the socket, so
39
+ # memcached may have applied it even though its reply was lost. Only a
40
+ # request that is safe to send twice is retried then.
41
+ def request_sent?
42
+ @request_sent
43
+ end
44
+ end
33
45
 
34
46
  # a multi-get reply named a key that wasn't requested, so the rest of that
35
47
  # server's replies can't be trusted
@@ -71,6 +83,7 @@ require_relative 'dalli/compressor'
71
83
  require_relative 'dalli/cache_result'
72
84
  require_relative 'dalli/client'
73
85
  require_relative 'dalli/key_manager'
86
+ require_relative 'dalli/key_grouping'
74
87
  require_relative 'dalli/pipelined_getter'
75
88
  require_relative 'dalli/pipelined_setter'
76
89
  require_relative 'dalli/pipelined_deleter'
@@ -46,7 +46,10 @@ module Rack
46
46
  #
47
47
  # It is not recommended to set `:expires_in`. Instead, use `:expire_after`,
48
48
  # which will control both the expiration of the client cookie as well
49
- # as the expiration of the corresponding entry in memcached.
49
+ # as the expiration of the corresponding entry in memcached. Without
50
+ # `:expire_after`, sessions are stored with no expiry: one stays in
51
+ # memcached until it's deleted (for example by a logout) or memcached
52
+ # evicts it, and a stolen session cookie keeps working until then.
50
53
  #
51
54
  # Rack::Session::Dalli also accepts a host of options that control how
52
55
  # the sessions and session cookies are managed, including the
@@ -144,6 +147,11 @@ module Rack
144
147
  sid.private_id if sid.respond_to?(:private_id)
145
148
  end
146
149
 
150
+ # A session that can't be decoded, for example after a change of
151
+ # serializer or a deploy that removed a class stored in it, is treated
152
+ # as missing, so the request gets a new session. Otherwise the session
153
+ # id couldn't be used or replaced, and the user couldn't log in until
154
+ # the cookie was cleared.
147
155
  def existing_session_for_sid(client, sid)
148
156
  return nil unless sid && !sid.empty?
149
157
 
@@ -151,6 +159,9 @@ module Rack
151
159
  return nil if key.nil?
152
160
 
153
161
  client.get(key)
162
+ rescue ::Dalli::UnmarshalError => e
163
+ ::Dalli.logger.warn("#{self.class} couldn't decode a stored session, so it started a new one: #{e.message}")
164
+ nil
154
165
  end
155
166
 
156
167
  def create_sid_with_empty_session(client)
@@ -184,12 +195,9 @@ module Rack
184
195
  end
185
196
  end
186
197
 
187
- # Rails stores an ActionDispatch::Request::Session::Options in the env, not a
188
- # Hash. It implements [] (and to_hash) but not Hash#slice, so slicing it
189
- # raised NoMethodError instead of returning this 401.
190
198
  def missing_session_response(env)
191
199
  options = env[Rack::RACK_SESSION_OPTIONS] || @default_options
192
- cookie = Rack::Utils.delete_set_cookie_header(key, { path: options[:path], domain: options[:domain] })
200
+ cookie = Rack::Utils.delete_set_cookie_header(key, options.slice(:path, :domain))
193
201
  [401, { 'content-type' => 'text/plain', 'set-cookie' => cookie }, ['Wrong session ID']]
194
202
  end
195
203
 
@@ -234,8 +242,6 @@ module Rack
234
242
  def with_dalli_client(result_on_error = nil, &)
235
243
  @data.with(&)
236
244
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
237
- raise if $ERROR_INFO.message.include?('undefined class')
238
-
239
245
  if $VERBOSE
240
246
  warn "#{self} is unable to find memcached server."
241
247
  warn $ERROR_INFO.inspect
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: 5.2.7
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
@@ -44,6 +44,7 @@ files:
44
44
  - lib/dalli/flags.rb
45
45
  - lib/dalli/instrumentation.rb
46
46
  - lib/dalli/json_serializer.rb
47
+ - lib/dalli/key_grouping.rb
47
48
  - lib/dalli/key_manager.rb
48
49
  - lib/dalli/options.rb
49
50
  - lib/dalli/pipelined_deleter.rb