dalli 5.2.6 → 5.3.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -0
- data/README.md +30 -0
- data/lib/dalli/client.rb +189 -76
- data/lib/dalli/key_grouping.rb +50 -0
- data/lib/dalli/key_manager.rb +30 -0
- data/lib/dalli/pipelined_deleter.rb +7 -16
- data/lib/dalli/pipelined_getter.rb +64 -58
- data/lib/dalli/pipelined_setter.rb +6 -15
- data/lib/dalli/protocol/base.rb +117 -41
- data/lib/dalli/protocol/connection_manager.rb +106 -2
- data/lib/dalli/protocol/key_regularizer.rb +5 -0
- data/lib/dalli/protocol/meta.rb +37 -9
- data/lib/dalli/protocol/request_formatter.rb +62 -33
- data/lib/dalli/protocol/response_processor.rb +49 -6
- data/lib/dalli/protocol/ttl_sanitizer.rb +13 -4
- data/lib/dalli/ring.rb +4 -0
- data/lib/dalli/servers_arg_normalizer.rb +19 -1
- data/lib/dalli/socket.rb +17 -2
- data/lib/dalli/version.rb +1 -1
- data/lib/dalli.rb +14 -1
- data/lib/rack/session/dalli.rb +16 -4
- metadata +2 -1
|
@@ -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
|
@@ -110,8 +110,18 @@ module Dalli
|
|
|
110
110
|
# OpenSSL can hold decrypted bytes that IO.select won't report as readable.
|
|
111
111
|
# With OpenSSL's default (no read-ahead) a short read has already used up
|
|
112
112
|
# the current record, so this is normally 0; it guards read-ahead setups.
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
#
|
|
114
|
+
# jruby-openssl can take the next record off the socket during a short
|
|
115
|
+
# read and hold it where neither pending nor IO.select sees it, so on
|
|
116
|
+
# JRuby a read continues until the socket would block.
|
|
117
|
+
if RUBY_ENGINE == 'jruby'
|
|
118
|
+
def buffered_data?
|
|
119
|
+
true
|
|
120
|
+
end
|
|
121
|
+
else
|
|
122
|
+
def buffered_data?
|
|
123
|
+
pending.positive?
|
|
124
|
+
end
|
|
115
125
|
end
|
|
116
126
|
|
|
117
127
|
unless method_defined?(:wait_readable)
|
|
@@ -182,6 +192,9 @@ module Dalli
|
|
|
182
192
|
end
|
|
183
193
|
|
|
184
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
|
|
185
198
|
configure_tcp_options(sock, options)
|
|
186
199
|
configure_socket_buffers(sock, options)
|
|
187
200
|
configure_timeout(sock, options)
|
|
@@ -307,6 +320,8 @@ module Dalli
|
|
|
307
320
|
end
|
|
308
321
|
|
|
309
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
|
|
310
325
|
# https://man7.org/linux/man-pages/man7/unix.7.html
|
|
311
326
|
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
|
|
312
327
|
sock.timeout = options[:socket_timeout] if options[:socket_timeout] && sock.respond_to?(:timeout=)
|
data/lib/dalli/version.rb
CHANGED
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
|
|
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'
|
data/lib/rack/session/dalli.rb
CHANGED
|
@@ -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,9 +195,12 @@ module Rack
|
|
|
184
195
|
end
|
|
185
196
|
end
|
|
186
197
|
|
|
198
|
+
# Rails stores an ActionDispatch::Request::Session::Options in the env, not a
|
|
199
|
+
# Hash. It implements [] (and to_hash) but not Hash#slice, so slicing it
|
|
200
|
+
# raised NoMethodError instead of returning this 401.
|
|
187
201
|
def missing_session_response(env)
|
|
188
202
|
options = env[Rack::RACK_SESSION_OPTIONS] || @default_options
|
|
189
|
-
cookie = Rack::Utils.delete_set_cookie_header(key, options
|
|
203
|
+
cookie = Rack::Utils.delete_set_cookie_header(key, { path: options[:path], domain: options[:domain] })
|
|
190
204
|
[401, { 'content-type' => 'text/plain', 'set-cookie' => cookie }, ['Wrong session ID']]
|
|
191
205
|
end
|
|
192
206
|
|
|
@@ -231,8 +245,6 @@ module Rack
|
|
|
231
245
|
def with_dalli_client(result_on_error = nil, &)
|
|
232
246
|
@data.with(&)
|
|
233
247
|
rescue ::Dalli::DalliError, Errno::ECONNREFUSED
|
|
234
|
-
raise if $ERROR_INFO.message.include?('undefined class')
|
|
235
|
-
|
|
236
248
|
if $VERBOSE
|
|
237
249
|
warn "#{self} is unable to find memcached server."
|
|
238
250
|
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.
|
|
4
|
+
version: 5.3.1
|
|
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
|