dalli 3.0.2 → 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.

data/lib/dalli/socket.rb CHANGED
@@ -4,56 +4,85 @@ require 'openssl'
4
4
  require 'rbconfig'
5
5
 
6
6
  module Dalli
7
+ ##
8
+ # Various socket implementations used by Dalli.
9
+ ##
7
10
  module Socket
11
+ ##
12
+ # Common methods for all socket implementations.
13
+ ##
8
14
  module InstanceMethods
9
-
10
15
  def readfull(count)
11
- value = +""
16
+ value = +''
12
17
  loop do
13
18
  result = read_nonblock(count - value.bytesize, exception: false)
14
- if result == :wait_readable
15
- raise Timeout::Error, "IO timeout: #{safe_options.inspect}" unless IO.select([self], nil, nil, options[:socket_timeout])
16
- elsif result == :wait_writable
17
- raise Timeout::Error, "IO timeout: #{safe_options.inspect}" unless IO.select(nil, [self], nil, options[:socket_timeout])
18
- elsif result
19
- value << result
20
- else
21
- raise Errno::ECONNRESET, "Connection reset: #{safe_options.inspect}"
22
- end
19
+ value << result if append_to_buffer?(result)
23
20
  break if value.bytesize == count
24
21
  end
25
22
  value
26
23
  end
27
24
 
28
25
  def read_available
29
- value = +""
26
+ value = +''
30
27
  loop do
31
28
  result = read_nonblock(8196, exception: false)
32
- if result == :wait_readable
33
- break
34
- elsif result == :wait_writable
35
- break
36
- elsif result
37
- value << result
38
- else
39
- raise Errno::ECONNRESET, "Connection reset: #{safe_options.inspect}"
40
- end
29
+ break if WAIT_RCS.include?(result)
30
+ raise Errno::ECONNRESET, "Connection reset: #{logged_options.inspect}" unless result
31
+
32
+ value << result
41
33
  end
42
34
  value
43
35
  end
44
36
 
45
- def safe_options
46
- options.reject { |k, v| [:username, :password].include? k }
37
+ WAIT_RCS = %i[wait_writable wait_readable].freeze
38
+
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
42
+
43
+ !WAIT_RCS.include?(result)
44
+ end
45
+
46
+ def nonblock_timed_out?(result)
47
+ return true if result == :wait_readable && !wait_readable(options[:socket_timeout])
48
+
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
52
+
53
+ FILTERED_OUT_OPTIONS = %i[username password].freeze
54
+ def logged_options
55
+ options.reject { |k, _| FILTERED_OUT_OPTIONS.include? k }
47
56
  end
48
57
  end
49
58
 
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
+ ##
50
64
  class SSLSocket < ::OpenSSL::SSL::SSLSocket
51
65
  include Dalli::Socket::InstanceMethods
52
66
  def options
53
67
  io.options
54
68
  end
69
+
70
+ unless method_defined?(:wait_readable)
71
+ def wait_readable(timeout = nil)
72
+ to_io.wait_readable(timeout)
73
+ end
74
+ end
75
+
76
+ unless method_defined?(:wait_writable)
77
+ def wait_writable(timeout = nil)
78
+ to_io.wait_writable(timeout)
79
+ end
80
+ end
55
81
  end
56
82
 
83
+ ##
84
+ # A standard TCP socket between the Dalli client and the Memcached server.
85
+ ##
57
86
  class TCP < TCPSocket
58
87
  include Dalli::Socket::InstanceMethods
59
88
  attr_accessor :options, :server
@@ -61,42 +90,57 @@ module Dalli
61
90
  def self.open(host, port, server, options = {})
62
91
  Timeout.timeout(options[:socket_timeout]) do
63
92
  sock = new(host, port)
64
- sock.options = {host: host, port: port}.merge(options)
93
+ sock.options = { host: host, port: port }.merge(options)
65
94
  sock.server = server
66
- sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
67
- sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options[:keepalive]
68
- sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVBUF, options[:rcvbuf]) if options[:rcvbuf]
69
- sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
70
-
71
- return sock unless options[:ssl_context]
72
-
73
- ssl_socket = Dalli::Socket::SSLSocket.new(sock, options[:ssl_context])
74
- ssl_socket.hostname = host
75
- ssl_socket.sync_close = true
76
- ssl_socket.connect
77
- ssl_socket
95
+ init_socket_options(sock, options)
96
+
97
+ options[:ssl_context] ? wrapping_ssl_socket(sock, host, options[:ssl_context]) : sock
78
98
  end
79
99
  end
80
- end
81
- end
82
100
 
83
- if RbConfig::CONFIG['host_os'] =~ /mingw|mswin/
84
- class Dalli::Socket::UNIX
85
- def initialize(*args)
86
- raise Dalli::DalliError, 'Unix sockets are not supported on Windows platform.'
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]
106
+ end
107
+
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
87
114
  end
88
115
  end
89
- else
90
- class Dalli::Socket::UNIX < UNIXSocket
91
- include Dalli::Socket::InstanceMethods
92
- attr_accessor :options, :server
93
116
 
94
- def self.open(path, server, options = {})
95
- Timeout.timeout(options[:socket_timeout]) do
96
- sock = new(path)
97
- sock.options = {path: path}.merge(options)
98
- sock.server = server
99
- sock
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
100
144
  end
101
145
  end
102
146
  end
data/lib/dalli/version.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dalli
4
- VERSION = "3.0.2"
4
+ VERSION = '3.0.6'
5
+
6
+ MIN_SUPPORTED_MEMCACHED_VERSION = '1.4'
5
7
  end
data/lib/dalli.rb CHANGED
@@ -1,30 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dalli/compressor"
4
- require "dalli/client"
5
- require "dalli/ring"
6
- require "dalli/protocol"
7
- require "dalli/protocol/binary"
8
- require "dalli/socket"
9
- require "dalli/version"
10
- require "dalli/options"
11
-
3
+ ##
4
+ # Namespace for all Dalli code.
5
+ ##
12
6
  module Dalli
13
- autoload :Server, "dalli/server"
7
+ autoload :Server, 'dalli/server'
14
8
 
15
9
  # generic error
16
10
  class DalliError < RuntimeError; end
11
+
17
12
  # socket/server communication error
18
13
  class NetworkError < DalliError; end
14
+
19
15
  # no server available/alive error
20
16
  class RingError < DalliError; end
17
+
21
18
  # application error in marshalling serialization
22
19
  class MarshalError < DalliError; end
20
+
23
21
  # application error in marshalling deserialization or decompression
24
22
  class UnmarshalError < DalliError; end
23
+
25
24
  # payload too big for memcached
26
25
  class ValueOverMaxSize < DalliError; end
27
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
+
28
31
  def self.logger
29
32
  @logger ||= (rails_logger || default_logger)
30
33
  end
@@ -35,7 +38,7 @@ module Dalli
35
38
  end
36
39
 
37
40
  def self.default_logger
38
- require "logger"
41
+ require 'logger'
39
42
  l = Logger.new($stdout)
40
43
  l.level = Logger::INFO
41
44
  l
@@ -45,3 +48,20 @@ module Dalli
45
48
  @logger = logger
46
49
  end
47
50
  end
51
+
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'
@@ -1,17 +1,23 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'rack/session/abstract/id'
3
4
  require 'dalli'
4
5
  require 'connection_pool'
6
+ require 'English'
5
7
 
6
8
  module Rack
7
9
  module Session
10
+ # Rack::Session::Dalli provides memcached based session management.
8
11
  class Dalli < Abstract::Persisted
9
12
  attr_reader :pool
10
13
 
14
+ # Don't freeze this until we fix the specs/implementation
15
+ # rubocop:disable Style/MutableConstant
11
16
  DEFAULT_DALLI_OPTIONS = {
12
- :namespace => 'rack:session',
13
- :memcache_server => 'localhost:11211'
17
+ namespace: 'rack:session',
18
+ memcache_server: 'localhost:11211'
14
19
  }
20
+ # rubocop:enable Style/MutableConstant
15
21
 
16
22
  # Brings in a new Rack::Session::Dalli middleware with the given
17
23
  # `:memcache_server`. The server is either a hostname, or a
@@ -67,7 +73,7 @@ module Rack
67
73
  # for more information about it and its default options (which would only
68
74
  # be applicable if you supplied one of the two options, but not both).
69
75
  #
70
- def initialize(app, options={})
76
+ def initialize(app, options = {})
71
77
  # Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
72
78
  super
73
79
 
@@ -80,10 +86,12 @@ module Rack
80
86
  @pool = ConnectionPool.new(popts || {}) { ::Dalli::Client.new(mserv, mopts) }
81
87
  end
82
88
 
83
- def get_session(env, sid)
89
+ def get_session(_env, sid)
84
90
  with_block([nil, {}]) do |dc|
85
- unless sid and !sid.empty? and session = dc.get(sid)
86
- old_sid, sid, session = sid, generate_sid_with(dc), {}
91
+ unless sid && !sid.empty? && (session = dc.get(sid))
92
+ old_sid = sid
93
+ sid = generate_sid_with(dc)
94
+ session = {}
87
95
  unless dc.add(sid, session, @default_ttl)
88
96
  sid = old_sid
89
97
  redo # generate a new sid and try again
@@ -93,7 +101,7 @@ module Rack
93
101
  end
94
102
  end
95
103
 
96
- def set_session(env, session_id, new_session, options)
104
+ def set_session(_env, session_id, new_session, options)
97
105
  return false unless session_id
98
106
 
99
107
  with_block(false) do |dc|
@@ -102,7 +110,7 @@ module Rack
102
110
  end
103
111
  end
104
112
 
105
- def destroy_session(env, session_id, options)
113
+ def destroy_session(_env, session_id, options)
106
114
  with_block do |dc|
107
115
  dc.delete(session_id)
108
116
  generate_sid_with(dc) unless options[:drop]
@@ -124,14 +132,15 @@ module Rack
124
132
  private
125
133
 
126
134
  def extract_dalli_options(options)
127
- raise "Rack::Session::Dalli no longer supports the :cache option." if options[:cache]
135
+ raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache]
128
136
 
129
- popts = {}
130
137
  # Filter out Rack::Session-specific options and apply our defaults
131
- mopts = DEFAULT_DALLI_OPTIONS.merge \
132
- options.reject {|k, _| DEFAULT_OPTIONS.key? k }
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)
133
141
  mserv = mopts.delete :memcache_server
134
142
 
143
+ popts = {}
135
144
  if mopts[:pool_size] || mopts[:pool_timeout]
136
145
  popts[:size] = mopts.delete :pool_size if mopts[:pool_size]
137
146
  popts[:timeout] = mopts.delete :pool_timeout if mopts[:pool_timeout]
@@ -141,20 +150,21 @@ module Rack
141
150
  [mserv, mopts, popts]
142
151
  end
143
152
 
144
- def generate_sid_with(dc)
145
- while true
153
+ def generate_sid_with(client)
154
+ loop do
146
155
  sid = generate_sid
147
- break sid unless dc.get(sid)
156
+ break sid unless client.get(sid)
148
157
  end
149
158
  end
150
159
 
151
- def with_block(default=nil, &block)
160
+ def with_block(default = nil, &block)
152
161
  @pool.with(&block)
153
162
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
154
- raise if $!.message =~ /undefined class/
163
+ raise if /undefined class/.match?($ERROR_INFO.message)
164
+
155
165
  if $VERBOSE
156
166
  warn "#{self} is unable to find memcached server."
157
- warn $!.inspect
167
+ warn $ERROR_INFO.inspect
158
168
  end
159
169
  default
160
170
  end
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: 3.0.2
4
+ version: 3.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-10-15 00:00:00.000000000 Z
12
+ date: 2021-11-30 00:00:00.000000000 Z
13
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'
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: rack
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -26,7 +40,21 @@ dependencies:
26
40
  - !ruby/object:Gem::Version
27
41
  version: '0'
28
42
  - !ruby/object:Gem::Dependency
29
- name: connection_pool
43
+ name: rubocop
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rubocop-minitest
30
58
  requirement: !ruby/object:Gem::Requirement
31
59
  requirements:
32
60
  - - ">="
@@ -40,7 +68,21 @@ dependencies:
40
68
  - !ruby/object:Gem::Version
41
69
  version: '0'
42
70
  - !ruby/object:Gem::Dependency
43
- name: openssl-extensions
71
+ name: rubocop-performance
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rubocop-rake
44
86
  requirement: !ruby/object:Gem::Requirement
45
87
  requirements:
46
88
  - - ">="
@@ -69,18 +111,29 @@ files:
69
111
  - lib/dalli/cas/client.rb
70
112
  - lib/dalli/client.rb
71
113
  - lib/dalli/compressor.rb
114
+ - lib/dalli/key_manager.rb
72
115
  - lib/dalli/options.rb
73
116
  - lib/dalli/protocol.rb
74
117
  - lib/dalli/protocol/binary.rb
118
+ - lib/dalli/protocol/binary/request_formatter.rb
119
+ - lib/dalli/protocol/binary/response_processor.rb
120
+ - lib/dalli/protocol/binary/sasl_authentication.rb
121
+ - lib/dalli/protocol/server_config_parser.rb
122
+ - lib/dalli/protocol/ttl_sanitizer.rb
123
+ - lib/dalli/protocol/value_compressor.rb
124
+ - lib/dalli/protocol/value_marshaller.rb
125
+ - lib/dalli/protocol/value_serializer.rb
75
126
  - lib/dalli/ring.rb
76
127
  - lib/dalli/server.rb
128
+ - lib/dalli/servers_arg_normalizer.rb
77
129
  - lib/dalli/socket.rb
78
130
  - lib/dalli/version.rb
79
131
  - lib/rack/session/dalli.rb
80
132
  homepage: https://github.com/petergoldstein/dalli
81
133
  licenses:
82
134
  - MIT
83
- metadata: {}
135
+ metadata:
136
+ rubygems_mfa_required: 'true'
84
137
  post_install_message:
85
138
  rdoc_options: []
86
139
  require_paths:
@@ -89,14 +142,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
142
  requirements:
90
143
  - - ">="
91
144
  - !ruby/object:Gem::Version
92
- version: '0'
145
+ version: '2.5'
93
146
  required_rubygems_version: !ruby/object:Gem::Requirement
94
147
  requirements:
95
148
  - - ">="
96
149
  - !ruby/object:Gem::Version
97
150
  version: '0'
98
151
  requirements: []
99
- rubygems_version: 3.2.29
152
+ rubygems_version: 3.2.31
100
153
  signing_key:
101
154
  specification_version: 4
102
155
  summary: High performance memcached client for Ruby