dalli 2.7.11 → 3.0.6
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.
- checksums.yaml +4 -4
- data/Gemfile +7 -6
- data/History.md +69 -0
- data/README.md +26 -200
- data/lib/dalli/cas/client.rb +1 -57
- data/lib/dalli/client.rb +272 -209
- data/lib/dalli/compressor.rb +12 -2
- data/lib/dalli/key_manager.rb +113 -0
- data/lib/dalli/options.rb +3 -4
- data/lib/dalli/protocol/binary/request_formatter.rb +109 -0
- data/lib/dalli/protocol/binary/response_processor.rb +149 -0
- data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
- data/lib/dalli/protocol/binary.rb +544 -0
- data/lib/dalli/protocol/server_config_parser.rb +84 -0
- data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
- data/lib/dalli/protocol/value_compressor.rb +85 -0
- data/lib/dalli/protocol/value_marshaller.rb +59 -0
- data/lib/dalli/protocol/value_serializer.rb +91 -0
- data/lib/dalli/protocol.rb +8 -0
- data/lib/dalli/ring.rb +86 -81
- data/lib/dalli/server.rb +3 -749
- data/lib/dalli/servers_arg_normalizer.rb +54 -0
- data/lib/dalli/socket.rb +115 -137
- data/lib/dalli/version.rb +4 -1
- data/lib/dalli.rb +32 -14
- data/lib/rack/session/dalli.rb +46 -55
- metadata +103 -10
- data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
- data/lib/active_support/cache/dalli_store.rb +0 -441
- data/lib/dalli/railtie.rb +0 -8
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dalli
|
4
|
+
##
|
5
|
+
# This module contains methods for validating and normalizing the servers
|
6
|
+
# argument passed to the client. This argument can be nil, a string, or
|
7
|
+
# an array of strings. Each string value in the argument can represent
|
8
|
+
# a single server or a comma separated list of servers.
|
9
|
+
#
|
10
|
+
# If nil, it falls back to the values of ENV['MEMCACHE_SERVERS'] if the latter is
|
11
|
+
# defined. If that environment value is not defined, a default of '127.0.0.1:11211'
|
12
|
+
# is used.
|
13
|
+
#
|
14
|
+
# A server config string can take one of three forms:
|
15
|
+
# * A colon separated string of (host, port, weight) where both port and
|
16
|
+
# weight are optional (e.g. 'localhost', 'abc.com:12345', 'example.org:22222:3')
|
17
|
+
# * A colon separated string of (UNIX socket, weight) where the weight is optional
|
18
|
+
# (e.g. '/var/run/memcached/socket', '/tmp/xyz:3') (not supported on Windows)
|
19
|
+
# * A URI with a 'memcached' protocol, which will typically include a username/password
|
20
|
+
#
|
21
|
+
# The methods in this module do not validate the format of individual server strings, but
|
22
|
+
# rather normalize the argument into a compact array, wherein each array entry corresponds
|
23
|
+
# to a single server config string. If that normalization is not possible, then an
|
24
|
+
# ArgumentError is thrown.
|
25
|
+
##
|
26
|
+
module ServersArgNormalizer
|
27
|
+
ENV_VAR_NAME = 'MEMCACHE_SERVERS'
|
28
|
+
DEFAULT_SERVERS = ['127.0.0.1:11211'].freeze
|
29
|
+
|
30
|
+
##
|
31
|
+
# Normalizes the argument into an array of servers.
|
32
|
+
# If the argument is a string, or an array containing strings, it's expected that the URIs are comma separated e.g.
|
33
|
+
# "memcache1.example.com:11211,memcache2.example.com:11211,memcache3.example.com:11211"
|
34
|
+
def self.normalize_servers(arg)
|
35
|
+
arg = apply_defaults(arg)
|
36
|
+
validate_type(arg)
|
37
|
+
Array(arg).flat_map { |s| s.split(',') }.reject(&:empty?)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.apply_defaults(arg)
|
41
|
+
return arg unless arg.nil?
|
42
|
+
|
43
|
+
ENV[ENV_VAR_NAME] || DEFAULT_SERVERS
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.validate_type(arg)
|
47
|
+
return if arg.is_a?(String)
|
48
|
+
return if arg.is_a?(Array) && arg.all?(String)
|
49
|
+
|
50
|
+
raise ArgumentError,
|
51
|
+
'An explicit servers argument must be a comma separated string or an array containing strings.'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/dalli/socket.rb
CHANGED
@@ -1,170 +1,148 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'rbconfig'
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
|
7
|
-
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if options[:keepalive]
|
8
|
-
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVBUF, options[:rcvbuf]) if options[:rcvbuf]
|
9
|
-
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
|
10
|
-
end
|
11
|
-
end
|
3
|
+
require 'openssl'
|
4
|
+
require 'rbconfig'
|
12
5
|
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
module Dalli
|
7
|
+
##
|
8
|
+
# Various socket implementations used by Dalli.
|
9
|
+
##
|
10
|
+
module Socket
|
11
|
+
##
|
12
|
+
# Common methods for all socket implementations.
|
13
|
+
##
|
14
|
+
module InstanceMethods
|
15
|
+
def readfull(count)
|
16
|
+
value = +''
|
17
|
+
loop do
|
18
|
+
result = read_nonblock(count - value.bytesize, exception: false)
|
19
|
+
value << result if append_to_buffer?(result)
|
20
|
+
break if value.bytesize == count
|
21
|
+
end
|
22
|
+
value
|
23
|
+
end
|
16
24
|
|
17
|
-
|
18
|
-
|
25
|
+
def read_available
|
26
|
+
value = +''
|
27
|
+
loop do
|
28
|
+
result = read_nonblock(8196, exception: false)
|
29
|
+
break if WAIT_RCS.include?(result)
|
30
|
+
raise Errno::ECONNRESET, "Connection reset: #{logged_options.inspect}" unless result
|
19
31
|
|
20
|
-
|
21
|
-
|
22
|
-
|
32
|
+
value << result
|
33
|
+
end
|
34
|
+
value
|
35
|
+
end
|
23
36
|
|
24
|
-
|
25
|
-
IO.select(nil, [self], nil, options[:socket_timeout]) || raise(Timeout::Error, "IO timeout")
|
26
|
-
end
|
37
|
+
WAIT_RCS = %i[wait_writable wait_readable].freeze
|
27
38
|
|
28
|
-
|
39
|
+
def append_to_buffer?(result)
|
40
|
+
raise Timeout::Error, "IO timeout: #{logged_options.inspect}" if nonblock_timed_out?(result)
|
41
|
+
raise Errno::ECONNRESET, "Connection reset: #{logged_options.inspect}" unless result
|
29
42
|
|
30
|
-
|
31
|
-
value = String.new('')
|
32
|
-
while true
|
33
|
-
value << kgio_read!(count - value.bytesize)
|
34
|
-
break if value.bytesize == count
|
43
|
+
!WAIT_RCS.include?(result)
|
35
44
|
end
|
36
|
-
value
|
37
|
-
end
|
38
45
|
|
39
|
-
|
40
|
-
|
41
|
-
while true
|
42
|
-
ret = kgio_tryread(8196)
|
43
|
-
case ret
|
44
|
-
when nil
|
45
|
-
raise EOFError, 'end of stream'
|
46
|
-
when :wait_readable
|
47
|
-
break
|
48
|
-
else
|
49
|
-
value << ret
|
50
|
-
end
|
51
|
-
end
|
52
|
-
value
|
53
|
-
end
|
54
|
-
end
|
46
|
+
def nonblock_timed_out?(result)
|
47
|
+
return true if result == :wait_readable && !wait_readable(options[:socket_timeout])
|
55
48
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def self.open(host, port, server, options = {})
|
60
|
-
addr = Socket.pack_sockaddr_in(port, host)
|
61
|
-
sock = start(addr)
|
62
|
-
setsockopts(sock, options)
|
63
|
-
sock.options = options
|
64
|
-
sock.server = server
|
65
|
-
sock.kgio_wait_writable
|
66
|
-
sock
|
67
|
-
rescue Timeout::Error
|
68
|
-
sock.close if sock
|
69
|
-
raise
|
70
|
-
end
|
71
|
-
end
|
49
|
+
# TODO: Do we actually need this? Looks to be only used in read_nonblock
|
50
|
+
result == :wait_writable && !wait_writable(options[:socket_timeout])
|
51
|
+
end
|
72
52
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
sock.options = options
|
78
|
-
sock.server = server
|
79
|
-
sock.kgio_wait_writable
|
80
|
-
sock
|
81
|
-
rescue Timeout::Error
|
82
|
-
sock.close if sock
|
83
|
-
raise
|
53
|
+
FILTERED_OUT_OPTIONS = %i[username password].freeze
|
54
|
+
def logged_options
|
55
|
+
options.reject { |k, _| FILTERED_OUT_OPTIONS.include? k }
|
56
|
+
end
|
84
57
|
end
|
85
|
-
end
|
86
|
-
|
87
|
-
if ::Kgio.respond_to?(:wait_readable=)
|
88
|
-
::Kgio.wait_readable = :kgio_wait_readable
|
89
|
-
::Kgio.wait_writable = :kgio_wait_writable
|
90
|
-
end
|
91
58
|
|
92
|
-
|
59
|
+
##
|
60
|
+
# Wraps the below TCP socket class in the case where the client
|
61
|
+
# has configured a TLS/SSL connection between Dalli and the
|
62
|
+
# Memcached server.
|
63
|
+
##
|
64
|
+
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
65
|
+
include Dalli::Socket::InstanceMethods
|
66
|
+
def options
|
67
|
+
io.options
|
68
|
+
end
|
93
69
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
def readfull(count)
|
98
|
-
value = String.new('')
|
99
|
-
begin
|
100
|
-
while true
|
101
|
-
value << read_nonblock(count - value.bytesize)
|
102
|
-
break if value.bytesize == count
|
103
|
-
end
|
104
|
-
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
105
|
-
if IO.select([self], nil, nil, options[:socket_timeout])
|
106
|
-
retry
|
107
|
-
else
|
108
|
-
safe_options = options.reject{|k,v| [:username, :password].include? k}
|
109
|
-
raise Timeout::Error, "IO timeout: #{safe_options.inspect}"
|
110
|
-
end
|
70
|
+
unless method_defined?(:wait_readable)
|
71
|
+
def wait_readable(timeout = nil)
|
72
|
+
to_io.wait_readable(timeout)
|
111
73
|
end
|
112
|
-
value
|
113
74
|
end
|
114
75
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
begin
|
119
|
-
value << read_nonblock(8196)
|
120
|
-
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
121
|
-
break
|
122
|
-
end
|
76
|
+
unless method_defined?(:wait_writable)
|
77
|
+
def wait_writable(timeout = nil)
|
78
|
+
to_io.wait_writable(timeout)
|
123
79
|
end
|
124
|
-
value
|
125
80
|
end
|
126
81
|
end
|
127
82
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
83
|
+
##
|
84
|
+
# A standard TCP socket between the Dalli client and the Memcached server.
|
85
|
+
##
|
86
|
+
class TCP < TCPSocket
|
87
|
+
include Dalli::Socket::InstanceMethods
|
88
|
+
attr_accessor :options, :server
|
89
|
+
|
90
|
+
def self.open(host, port, server, options = {})
|
91
|
+
Timeout.timeout(options[:socket_timeout]) do
|
92
|
+
sock = new(host, port)
|
93
|
+
sock.options = { host: host, port: port }.merge(options)
|
94
|
+
sock.server = server
|
95
|
+
init_socket_options(sock, options)
|
133
96
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
sock
|
141
|
-
|
142
|
-
sock.
|
143
|
-
sock.server = server
|
144
|
-
sock
|
97
|
+
options[:ssl_context] ? wrapping_ssl_socket(sock, host, options[:ssl_context]) : sock
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.init_socket_options(sock, options)
|
102
|
+
sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
|
103
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options[:keepalive]
|
104
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVBUF, options[:rcvbuf]) if options[:rcvbuf]
|
105
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
|
145
106
|
end
|
146
|
-
end
|
147
|
-
end
|
148
107
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
108
|
+
def self.wrapping_ssl_socket(tcp_socket, host, ssl_context)
|
109
|
+
ssl_socket = Dalli::Socket::SSLSocket.new(tcp_socket, ssl_context)
|
110
|
+
ssl_socket.hostname = host
|
111
|
+
ssl_socket.sync_close = true
|
112
|
+
ssl_socket.connect
|
113
|
+
ssl_socket
|
153
114
|
end
|
154
115
|
end
|
155
|
-
else
|
156
|
-
class Dalli::Server::KSocket::UNIX < UNIXSocket
|
157
|
-
include Dalli::Server::KSocket
|
158
116
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
117
|
+
if /mingw|mswin/.match?(RbConfig::CONFIG['host_os'])
|
118
|
+
##
|
119
|
+
# UNIX domain sockets are not supported on Windows platforms.
|
120
|
+
##
|
121
|
+
class UNIX
|
122
|
+
def initialize(*_args)
|
123
|
+
raise Dalli::DalliError, 'Unix sockets are not supported on Windows platform.'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
else
|
127
|
+
|
128
|
+
##
|
129
|
+
# UNIX represents a UNIX domain socket, which is an interprocess communication
|
130
|
+
# mechanism between processes on the same host. Used when the Memcached server
|
131
|
+
# is running on the same machine as the Dalli client.
|
132
|
+
##
|
133
|
+
class UNIX < UNIXSocket
|
134
|
+
include Dalli::Socket::InstanceMethods
|
135
|
+
attr_accessor :options, :server
|
136
|
+
|
137
|
+
def self.open(path, server, options = {})
|
138
|
+
Timeout.timeout(options[:socket_timeout]) do
|
139
|
+
sock = new(path)
|
140
|
+
sock.options = { path: path }.merge(options)
|
141
|
+
sock.server = server
|
142
|
+
sock
|
143
|
+
end
|
165
144
|
end
|
166
145
|
end
|
167
146
|
end
|
168
|
-
|
169
147
|
end
|
170
148
|
end
|
data/lib/dalli/version.rb
CHANGED
data/lib/dalli.rb
CHANGED
@@ -1,39 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'dalli/compressor'
|
3
|
-
require 'dalli/client'
|
4
|
-
require 'dalli/ring'
|
5
|
-
require 'dalli/server'
|
6
|
-
require 'dalli/socket'
|
7
|
-
require 'dalli/version'
|
8
|
-
require 'dalli/options'
|
9
|
-
require 'dalli/railtie' if defined?(::Rails::Railtie)
|
10
2
|
|
3
|
+
##
|
4
|
+
# Namespace for all Dalli code.
|
5
|
+
##
|
11
6
|
module Dalli
|
7
|
+
autoload :Server, 'dalli/server'
|
8
|
+
|
12
9
|
# generic error
|
13
10
|
class DalliError < RuntimeError; end
|
11
|
+
|
14
12
|
# socket/server communication error
|
15
13
|
class NetworkError < DalliError; end
|
14
|
+
|
16
15
|
# no server available/alive error
|
17
16
|
class RingError < DalliError; end
|
17
|
+
|
18
18
|
# application error in marshalling serialization
|
19
19
|
class MarshalError < DalliError; end
|
20
|
+
|
20
21
|
# application error in marshalling deserialization or decompression
|
21
22
|
class UnmarshalError < DalliError; end
|
23
|
+
|
22
24
|
# payload too big for memcached
|
23
25
|
class ValueOverMaxSize < DalliError; end
|
24
26
|
|
27
|
+
# Implements the NullObject pattern to store an application-defined value for 'Key not found' responses.
|
28
|
+
class NilObject; end # rubocop:disable Lint/EmptyClass
|
29
|
+
NOT_FOUND = NilObject.new
|
30
|
+
|
25
31
|
def self.logger
|
26
32
|
@logger ||= (rails_logger || default_logger)
|
27
33
|
end
|
28
34
|
|
29
35
|
def self.rails_logger
|
30
36
|
(defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
|
31
|
-
|
37
|
+
(defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
|
32
38
|
end
|
33
39
|
|
34
40
|
def self.default_logger
|
35
41
|
require 'logger'
|
36
|
-
l = Logger.new(
|
42
|
+
l = Logger.new($stdout)
|
37
43
|
l.level = Logger::INFO
|
38
44
|
l
|
39
45
|
end
|
@@ -41,9 +47,21 @@ module Dalli
|
|
41
47
|
def self.logger=(logger)
|
42
48
|
@logger = logger
|
43
49
|
end
|
44
|
-
|
45
50
|
end
|
46
51
|
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
require_relative 'dalli/version'
|
53
|
+
|
54
|
+
require_relative 'dalli/compressor'
|
55
|
+
require_relative 'dalli/client'
|
56
|
+
require_relative 'dalli/key_manager'
|
57
|
+
require_relative 'dalli/ring'
|
58
|
+
require_relative 'dalli/protocol'
|
59
|
+
require_relative 'dalli/protocol/binary'
|
60
|
+
require_relative 'dalli/protocol/server_config_parser'
|
61
|
+
require_relative 'dalli/protocol/ttl_sanitizer'
|
62
|
+
require_relative 'dalli/protocol/value_compressor'
|
63
|
+
require_relative 'dalli/protocol/value_marshaller'
|
64
|
+
require_relative 'dalli/protocol/value_serializer'
|
65
|
+
require_relative 'dalli/servers_arg_normalizer'
|
66
|
+
require_relative 'dalli/socket'
|
67
|
+
require_relative 'dalli/options'
|
data/lib/rack/session/dalli.rb
CHANGED
@@ -1,16 +1,23 @@
|
|
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
|
-
|
8
|
-
|
10
|
+
# Rack::Session::Dalli provides memcached based session management.
|
11
|
+
class Dalli < Abstract::Persisted
|
12
|
+
attr_reader :pool
|
9
13
|
|
14
|
+
# Don't freeze this until we fix the specs/implementation
|
15
|
+
# rubocop:disable Style/MutableConstant
|
10
16
|
DEFAULT_DALLI_OPTIONS = {
|
11
|
-
:
|
12
|
-
:
|
17
|
+
namespace: 'rack:session',
|
18
|
+
memcache_server: 'localhost:11211'
|
13
19
|
}
|
20
|
+
# rubocop:enable Style/MutableConstant
|
14
21
|
|
15
22
|
# Brings in a new Rack::Session::Dalli middleware with the given
|
16
23
|
# `:memcache_server`. The server is either a hostname, or a
|
@@ -66,7 +73,7 @@ module Rack
|
|
66
73
|
# for more information about it and its default options (which would only
|
67
74
|
# be applicable if you supplied one of the two options, but not both).
|
68
75
|
#
|
69
|
-
def initialize(app, options={})
|
76
|
+
def initialize(app, options = {})
|
70
77
|
# Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
|
71
78
|
super
|
72
79
|
|
@@ -74,28 +81,17 @@ module Rack
|
|
74
81
|
@default_ttl = ttl @default_options[:expire_after]
|
75
82
|
|
76
83
|
# Normalize and validate passed options
|
77
|
-
|
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
|
84
|
+
mserv, mopts, popts = extract_dalli_options(options)
|
90
85
|
|
91
|
-
|
92
|
-
end
|
86
|
+
@pool = ConnectionPool.new(popts || {}) { ::Dalli::Client.new(mserv, mopts) }
|
93
87
|
end
|
94
88
|
|
95
|
-
def get_session(
|
96
|
-
with_block(
|
97
|
-
unless sid
|
98
|
-
old_sid
|
89
|
+
def get_session(_env, sid)
|
90
|
+
with_block([nil, {}]) do |dc|
|
91
|
+
unless sid && !sid.empty? && (session = dc.get(sid))
|
92
|
+
old_sid = sid
|
93
|
+
sid = generate_sid_with(dc)
|
94
|
+
session = {}
|
99
95
|
unless dc.add(sid, session, @default_ttl)
|
100
96
|
sid = old_sid
|
101
97
|
redo # generate a new sid and try again
|
@@ -105,77 +101,72 @@ module Rack
|
|
105
101
|
end
|
106
102
|
end
|
107
103
|
|
108
|
-
def set_session(
|
104
|
+
def set_session(_env, session_id, new_session, options)
|
109
105
|
return false unless session_id
|
110
106
|
|
111
|
-
with_block(
|
107
|
+
with_block(false) do |dc|
|
112
108
|
dc.set(session_id, new_session, ttl(options[:expire_after]))
|
113
109
|
session_id
|
114
110
|
end
|
115
111
|
end
|
116
112
|
|
117
|
-
def destroy_session(
|
118
|
-
with_block
|
113
|
+
def destroy_session(_env, session_id, options)
|
114
|
+
with_block do |dc|
|
119
115
|
dc.delete(session_id)
|
120
116
|
generate_sid_with(dc) unless options[:drop]
|
121
117
|
end
|
122
118
|
end
|
123
119
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
end
|
120
|
+
def find_session(req, sid)
|
121
|
+
get_session req.env, sid
|
122
|
+
end
|
128
123
|
|
129
|
-
|
130
|
-
|
131
|
-
|
124
|
+
def write_session(req, sid, session, options)
|
125
|
+
set_session req.env, sid, session, options
|
126
|
+
end
|
132
127
|
|
133
|
-
|
134
|
-
|
135
|
-
end
|
128
|
+
def delete_session(req, sid, options)
|
129
|
+
destroy_session req.env, sid, options
|
136
130
|
end
|
137
131
|
|
138
132
|
private
|
139
133
|
|
140
134
|
def extract_dalli_options(options)
|
141
|
-
|
135
|
+
raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache]
|
142
136
|
|
143
137
|
# Filter out Rack::Session-specific options and apply our defaults
|
144
|
-
|
145
|
-
|
138
|
+
# Filter out Rack::Session-specific options and apply our defaults
|
139
|
+
filtered_opts = options.reject { |k, _| DEFAULT_OPTIONS.key? k }
|
140
|
+
mopts = DEFAULT_DALLI_OPTIONS.merge(filtered_opts)
|
146
141
|
mserv = mopts.delete :memcache_server
|
147
142
|
|
143
|
+
popts = {}
|
148
144
|
if mopts[:pool_size] || mopts[:pool_timeout]
|
149
|
-
popts = {}
|
150
145
|
popts[:size] = mopts.delete :pool_size if mopts[:pool_size]
|
151
146
|
popts[:timeout] = mopts.delete :pool_timeout if mopts[:pool_timeout]
|
152
|
-
|
153
|
-
# For a connection pool, locking is handled at the pool level
|
154
|
-
mopts[:threadsafe] = false unless mopts.key? :threadsafe
|
147
|
+
mopts[:threadsafe] = true
|
155
148
|
end
|
156
149
|
|
157
|
-
[
|
150
|
+
[mserv, mopts, popts]
|
158
151
|
end
|
159
152
|
|
160
|
-
def generate_sid_with(
|
161
|
-
|
153
|
+
def generate_sid_with(client)
|
154
|
+
loop do
|
162
155
|
sid = generate_sid
|
163
|
-
break sid unless
|
156
|
+
break sid unless client.get(sid)
|
164
157
|
end
|
165
158
|
end
|
166
159
|
|
167
|
-
def with_block(
|
168
|
-
@mutex.lock if @mutex and env['rack.multithread']
|
160
|
+
def with_block(default = nil, &block)
|
169
161
|
@pool.with(&block)
|
170
162
|
rescue ::Dalli::DalliError, Errno::ECONNREFUSED
|
171
|
-
raise if
|
163
|
+
raise if /undefined class/.match?($ERROR_INFO.message)
|
164
|
+
|
172
165
|
if $VERBOSE
|
173
166
|
warn "#{self} is unable to find memcached server."
|
174
|
-
warn
|
167
|
+
warn $ERROR_INFO.inspect
|
175
168
|
end
|
176
169
|
default
|
177
|
-
ensure
|
178
|
-
@mutex.unlock if @mutex and @mutex.locked?
|
179
170
|
end
|
180
171
|
|
181
172
|
def ttl(expire_after)
|