dalli 2.7.5 → 3.0.1

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
@@ -1,162 +1,95 @@
1
- require 'rbconfig'
1
+ # frozen_string_literal: true
2
2
 
3
- module Dalli::Server::TCPSocketOptions
4
- def setsockopts(sock, options)
5
- sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
6
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if options[:keepalive]
7
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVBUF, options[:rcvbuf]) if options[:rcvbuf]
8
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
9
- end
10
- end
11
-
12
- begin
13
- require 'kgio'
14
- puts "Using kgio socket IO" if defined?($TESTING) && $TESTING
15
-
16
- class Dalli::Server::KSocket < Kgio::Socket
17
- attr_accessor :options, :server
18
-
19
- def kgio_wait_readable
20
- IO.select([self], nil, nil, options[:socket_timeout]) || raise(Timeout::Error, "IO timeout")
21
- end
22
-
23
- def kgio_wait_writable
24
- IO.select(nil, [self], nil, options[:socket_timeout]) || raise(Timeout::Error, "IO timeout")
25
- end
26
-
27
- alias :write :kgio_write
28
-
29
- def readfull(count)
30
- value = ''
31
- loop do
32
- value << kgio_read!(count - value.bytesize)
33
- break if value.bytesize == count
34
- end
35
- value
36
- end
37
-
38
- def read_available
39
- value = ''
40
- loop do
41
- ret = kgio_tryread(8196)
42
- case ret
43
- when nil
44
- raise EOFError, 'end of stream'
45
- when :wait_readable
46
- break
47
- else
48
- value << ret
49
- end
50
- end
51
- value
52
- end
53
- end
3
+ require 'openssl'
54
4
 
55
- class Dalli::Server::KSocket::TCP < Dalli::Server::KSocket
56
- extend Dalli::Server::TCPSocketOptions
57
-
58
- def self.open(host, port, server, options = {})
59
- addr = Socket.pack_sockaddr_in(port, host)
60
- sock = start(addr)
61
- setsockopts(sock, options)
62
- sock.options = options
63
- sock.server = server
64
- sock.kgio_wait_writable
65
- sock
66
- end
67
- end
68
-
69
- class Dalli::Server::KSocket::UNIX < Dalli::Server::KSocket
70
- def self.open(path, server, options = {})
71
- addr = Socket.pack_sockaddr_un(path)
72
- sock = start(addr)
73
- sock.options = options
74
- sock.server = server
75
- sock.kgio_wait_writable
76
- sock
77
- end
78
- end
79
-
80
- if ::Kgio.respond_to?(:wait_readable=)
81
- ::Kgio.wait_readable = :kgio_wait_readable
82
- ::Kgio.wait_writable = :kgio_wait_writable
83
- end
84
-
85
- rescue LoadError
86
-
87
- puts "Using standard socket IO (#{RUBY_DESCRIPTION})" if defined?($TESTING) && $TESTING
88
- module Dalli::Server::KSocket
5
+ module Dalli
6
+ module Socket
89
7
  module InstanceMethods
8
+
90
9
  def readfull(count)
91
- value = ''
92
- begin
93
- loop do
94
- value << read_nonblock(count - value.bytesize)
95
- break if value.bytesize == count
96
- end
97
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK
98
- if IO.select([self], nil, nil, options[:socket_timeout])
99
- retry
10
+ value = +""
11
+ loop do
12
+ result = read_nonblock(count - value.bytesize, exception: false)
13
+ if result == :wait_readable
14
+ raise Timeout::Error, "IO timeout: #{safe_options.inspect}" unless IO.select([self], nil, nil, options[:socket_timeout])
15
+ elsif result == :wait_writable
16
+ raise Timeout::Error, "IO timeout: #{safe_options.inspect}" unless IO.select(nil, [self], nil, options[:socket_timeout])
17
+ elsif result
18
+ value << result
100
19
  else
101
- raise Timeout::Error, "IO timeout: #{options.inspect}"
20
+ raise Errno::ECONNRESET, "Connection reset: #{safe_options.inspect}"
102
21
  end
22
+ break if value.bytesize == count
103
23
  end
104
24
  value
105
25
  end
106
26
 
107
27
  def read_available
108
- value = ''
28
+ value = +""
109
29
  loop do
110
- begin
111
- value << read_nonblock(8196)
112
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK
30
+ result = read_nonblock(8196, exception: false)
31
+ if result == :wait_readable
32
+ break
33
+ elsif result == :wait_writable
113
34
  break
35
+ elsif result
36
+ value << result
37
+ else
38
+ raise Errno::ECONNRESET, "Connection reset: #{safe_options.inspect}"
114
39
  end
115
40
  end
116
41
  value
117
42
  end
118
- end
119
43
 
120
- def self.included(receiver)
121
- receiver.send(:attr_accessor, :options, :server)
122
- receiver.send(:include, InstanceMethods)
44
+ def safe_options
45
+ options.reject { |k, v| [:username, :password].include? k }
46
+ end
123
47
  end
124
- end
125
-
126
- class Dalli::Server::KSocket::TCP < TCPSocket
127
- extend Dalli::Server::TCPSocketOptions
128
- include Dalli::Server::KSocket
129
48
 
130
- def self.open(host, port, server, options = {})
131
- Timeout.timeout(options[:socket_timeout]) do
132
- sock = new(host, port)
133
- setsockopts(sock, options)
134
- sock.options = {:host => host, :port => port}.merge(options)
135
- sock.server = server
136
- sock
49
+ class SSLSocket < ::OpenSSL::SSL::SSLSocket
50
+ include Dalli::Socket::InstanceMethods
51
+ def options
52
+ io.options
137
53
  end
138
54
  end
139
- end
140
55
 
141
- if RbConfig::CONFIG['host_os'] =~ /mingw|mswin/
142
- class Dalli::Server::KSocket::UNIX
143
- def initialize(*args)
144
- raise Dalli::DalliError, "Unix sockets are not supported on Windows platform."
56
+ class TCP < TCPSocket
57
+ include Dalli::Socket::InstanceMethods
58
+ attr_accessor :options, :server
59
+
60
+ def self.open(host, port, server, options = {})
61
+ Timeout.timeout(options[:socket_timeout]) do
62
+ sock = new(host, port)
63
+ sock.options = {host: host, port: port}.merge(options)
64
+ sock.server = server
65
+ sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
66
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options[:keepalive]
67
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVBUF, options[:rcvbuf]) if options[:rcvbuf]
68
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
69
+
70
+ return sock unless options[:ssl_context]
71
+
72
+ ssl_socket = Dalli::Socket::SSLSocket.new(sock, options[:ssl_context])
73
+ ssl_socket.hostname = host
74
+ ssl_socket.sync_close = true
75
+ ssl_socket.connect
76
+ ssl_socket
77
+ end
145
78
  end
146
79
  end
147
- else
148
- class Dalli::Server::KSocket::UNIX < UNIXSocket
149
- include Dalli::Server::KSocket
80
+
81
+ class UNIX < UNIXSocket
82
+ include Dalli::Socket::InstanceMethods
83
+ attr_accessor :options, :server
150
84
 
151
85
  def self.open(path, server, options = {})
152
86
  Timeout.timeout(options[:socket_timeout]) do
153
87
  sock = new(path)
154
- sock.options = {:path => path}.merge(options)
88
+ sock.options = {path: path}.merge(options)
155
89
  sock.server = server
156
90
  sock
157
91
  end
158
92
  end
159
93
  end
160
-
161
94
  end
162
95
  end
data/lib/dalli/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dalli
2
- VERSION = '2.7.5'
4
+ VERSION = "3.0.1"
3
5
  end
data/lib/dalli.rb CHANGED
@@ -1,13 +1,17 @@
1
- require 'dalli/compressor'
2
- require 'dalli/client'
3
- require 'dalli/ring'
4
- require 'dalli/server'
5
- require 'dalli/socket'
6
- require 'dalli/version'
7
- require 'dalli/options'
8
- require 'dalli/railtie' if defined?(::Rails::Railtie)
1
+ # frozen_string_literal: true
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"
9
11
 
10
12
  module Dalli
13
+ autoload :Server, "dalli/server"
14
+
11
15
  # generic error
12
16
  class DalliError < RuntimeError; end
13
17
  # socket/server communication error
@@ -18,6 +22,8 @@ module Dalli
18
22
  class MarshalError < DalliError; end
19
23
  # application error in marshalling deserialization or decompression
20
24
  class UnmarshalError < DalliError; end
25
+ # payload too big for memcached
26
+ class ValueOverMaxSize < DalliError; end
21
27
 
22
28
  def self.logger
23
29
  @logger ||= (rails_logger || default_logger)
@@ -25,12 +31,12 @@ module Dalli
25
31
 
26
32
  def self.rails_logger
27
33
  (defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
28
- (defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
34
+ (defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
29
35
  end
30
36
 
31
37
  def self.default_logger
32
- require 'logger'
33
- l = Logger.new(STDOUT)
38
+ require "logger"
39
+ l = Logger.new($stdout)
34
40
  l.level = Logger::INFO
35
41
  l
36
42
  end
@@ -38,9 +44,4 @@ module Dalli
38
44
  def self.logger=(logger)
39
45
  @logger = logger
40
46
  end
41
-
42
- end
43
-
44
- if defined?(RAILS_VERSION) && RAILS_VERSION < '3'
45
- raise Dalli::DalliError, "Dalli #{Dalli::VERSION} does not support Rails version < 3.0"
46
47
  end
@@ -1,37 +1,92 @@
1
+ # frozen_string_literal: true
1
2
  require 'rack/session/abstract/id'
2
3
  require 'dalli'
4
+ require 'connection_pool'
3
5
 
4
6
  module Rack
5
7
  module Session
6
- class Dalli < Abstract::ID
7
- attr_reader :pool, :mutex
8
+ class Dalli < Abstract::Persisted
9
+ attr_reader :pool
8
10
 
9
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
11
+ DEFAULT_DALLI_OPTIONS = {
10
12
  :namespace => 'rack:session',
11
13
  :memcache_server => 'localhost:11211'
14
+ }
12
15
 
16
+ # Brings in a new Rack::Session::Dalli middleware with the given
17
+ # `:memcache_server`. The server is either a hostname, or a
18
+ # host-with-port string in the form of "host_name:port", or an array of
19
+ # such strings. For example:
20
+ #
21
+ # use Rack::Session::Dalli,
22
+ # :memcache_server => "mc.example.com:1234"
23
+ #
24
+ # If no `:memcache_server` option is specified, Rack::Session::Dalli will
25
+ # connect to localhost, port 11211 (the default memcached port). If
26
+ # `:memcache_server` is set to nil, Dalli::Client will look for
27
+ # ENV['MEMCACHE_SERVERS'] and use that value if it is available, or fall
28
+ # back to the same default behavior described above.
29
+ #
30
+ # Rack::Session::Dalli is intended to be a drop-in replacement for
31
+ # Rack::Session::Memcache. It accepts additional options that control the
32
+ # behavior of Rack::Session, Dalli::Client, and an optional
33
+ # ConnectionPool. First and foremost, if you wish to instantiate your own
34
+ # Dalli::Client (or ConnectionPool) and use that instead of letting
35
+ # Rack::Session::Dalli instantiate it on your behalf, simply pass it in
36
+ # as the `:cache` option. Please note that you will be responsible for
37
+ # setting the namespace and any other options on Dalli::Client.
38
+ #
39
+ # Secondly, if you're not using the `:cache` option, Rack::Session::Dalli
40
+ # accepts the same options as Dalli::Client, so it's worth reviewing its
41
+ # documentation. Perhaps most importantly, if you don't specify a
42
+ # `:namespace` option, Rack::Session::Dalli will default to using
43
+ # "rack:session".
44
+ #
45
+ # Whether you are using the `:cache` option or not, it is not recommend
46
+ # to set `:expires_in`. Instead, use `:expire_after`, which will control
47
+ # both the expiration of the client cookie as well as the expiration of
48
+ # the corresponding entry in memcached.
49
+ #
50
+ # Rack::Session::Dalli also accepts a host of options that control how
51
+ # the sessions and session cookies are managed, including the
52
+ # aforementioned `:expire_after` option. Please see the documentation for
53
+ # Rack::Session::Abstract::Persisted for a detailed explanation of these
54
+ # options and their default values.
55
+ #
56
+ # Finally, if your web application is multithreaded, the
57
+ # Rack::Session::Dalli middleware can become a source of contention. You
58
+ # can use a connection pool of Dalli clients by passing in the
59
+ # `:pool_size` and/or `:pool_timeout` options. For example:
60
+ #
61
+ # use Rack::Session::Dalli,
62
+ # :memcache_server => "mc.example.com:1234",
63
+ # :pool_size => 10
64
+ #
65
+ # You must include the `connection_pool` gem in your project if you wish
66
+ # to use pool support. Please see the documentation for ConnectionPool
67
+ # for more information about it and its default options (which would only
68
+ # be applicable if you supplied one of the two options, but not both).
69
+ #
13
70
  def initialize(app, options={})
71
+ # Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
14
72
  super
15
- @mutex = Mutex.new
16
- mserv = @default_options[:memcache_server]
17
- mopts = @default_options.reject{|k,v| !DEFAULT_OPTIONS.include? k }
18
- @pool = options[:cache] || ::Dalli::Client.new(mserv, mopts)
19
- @pool.alive!
20
- end
21
73
 
22
- def generate_sid
23
- loop do
24
- sid = super
25
- break sid unless @pool.get(sid)
26
- end
74
+ # Determine the default TTL for newly-created sessions
75
+ @default_ttl = ttl @default_options[:expire_after]
76
+
77
+ # Normalize and validate passed options
78
+ mserv, mopts, popts = extract_dalli_options(options)
79
+
80
+ @pool = ConnectionPool.new(popts || {}) { ::Dalli::Client.new(mserv, mopts) }
27
81
  end
28
82
 
29
83
  def get_session(env, sid)
30
- with_lock(env, [nil, {}]) do
31
- unless sid and !sid.empty? and session = @pool.get(sid)
32
- sid, session = generate_sid, {}
33
- unless @pool.add(sid, session)
34
- raise "Session collision on '#{sid.inspect}'"
84
+ 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), {}
87
+ unless dc.add(sid, session, @default_ttl)
88
+ sid = old_sid
89
+ redo # generate a new sid and try again
35
90
  end
36
91
  end
37
92
  [sid, session]
@@ -40,25 +95,61 @@ module Rack
40
95
 
41
96
  def set_session(env, session_id, new_session, options)
42
97
  return false unless session_id
43
- expiry = options[:expire_after]
44
- expiry = expiry.nil? ? 0 : expiry + 1
45
98
 
46
- with_lock(env, false) do
47
- @pool.set session_id, new_session, expiry
99
+ with_block(false) do |dc|
100
+ dc.set(session_id, new_session, ttl(options[:expire_after]))
48
101
  session_id
49
102
  end
50
103
  end
51
104
 
52
105
  def destroy_session(env, session_id, options)
53
- with_lock(env) do
54
- @pool.delete(session_id)
55
- generate_sid unless options[:drop]
106
+ with_block do |dc|
107
+ dc.delete(session_id)
108
+ generate_sid_with(dc) unless options[:drop]
56
109
  end
57
110
  end
58
111
 
59
- def with_lock(env, default=nil)
60
- @mutex.lock if env['rack.multithread']
61
- yield
112
+ def find_session(req, sid)
113
+ get_session req.env, sid
114
+ end
115
+
116
+ def write_session(req, sid, session, options)
117
+ set_session req.env, sid, session, options
118
+ end
119
+
120
+ def delete_session(req, sid, options)
121
+ destroy_session req.env, sid, options
122
+ end
123
+
124
+ private
125
+
126
+ def extract_dalli_options(options)
127
+ raise "Rack::Session::Dalli no longer supports the :cache option." if options[:cache]
128
+
129
+ popts = {}
130
+ # Filter out Rack::Session-specific options and apply our defaults
131
+ mopts = DEFAULT_DALLI_OPTIONS.merge \
132
+ options.reject {|k, _| DEFAULT_OPTIONS.key? k }
133
+ mserv = mopts.delete :memcache_server
134
+
135
+ if mopts[:pool_size] || mopts[:pool_timeout]
136
+ popts[:size] = mopts.delete :pool_size if mopts[:pool_size]
137
+ popts[:timeout] = mopts.delete :pool_timeout if mopts[:pool_timeout]
138
+ mopts[:threadsafe] = true
139
+ end
140
+
141
+ [mserv, mopts, popts]
142
+ end
143
+
144
+ def generate_sid_with(dc)
145
+ while true
146
+ sid = generate_sid
147
+ break sid unless dc.get(sid)
148
+ end
149
+ end
150
+
151
+ def with_block(default=nil, &block)
152
+ @pool.with(&block)
62
153
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
63
154
  raise if $!.message =~ /undefined class/
64
155
  if $VERBOSE
@@ -66,10 +157,11 @@ module Rack
66
157
  warn $!.inspect
67
158
  end
68
159
  default
69
- ensure
70
- @mutex.unlock if @mutex.locked?
71
160
  end
72
161
 
162
+ def ttl(expire_after)
163
+ expire_after.nil? ? 0 : expire_after + 1
164
+ end
73
165
  end
74
166
  end
75
167
  end
metadata CHANGED
@@ -1,74 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dalli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.5
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
8
8
  - Mike Perham
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-12-16 00:00:00.000000000 Z
12
+ date: 2021-10-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: minitest
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: 4.2.0
21
- type: :development
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: 4.2.0
28
- - !ruby/object:Gem::Dependency
29
- name: mocha
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: '0'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: '0'
42
- - !ruby/object:Gem::Dependency
43
- name: rails
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - "~>"
47
- - !ruby/object:Gem::Version
48
- version: '4'
49
- type: :development
50
- prerelease: false
51
- version_requirements: !ruby/object:Gem::Requirement
52
- requirements:
53
- - - "~>"
54
- - !ruby/object:Gem::Version
55
- version: '4'
56
- - !ruby/object:Gem::Dependency
57
- name: rake
58
- requirement: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: '0'
63
- type: :development
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
- - !ruby/object:Gem::Dependency
71
- name: appraisal
15
+ name: rack
72
16
  requirement: !ruby/object:Gem::Requirement
73
17
  requirements:
74
18
  - - ">="
@@ -96,21 +40,7 @@ dependencies:
96
40
  - !ruby/object:Gem::Version
97
41
  version: '0'
98
42
  - !ruby/object:Gem::Dependency
99
- name: rdoc
100
- requirement: !ruby/object:Gem::Requirement
101
- requirements:
102
- - - ">="
103
- - !ruby/object:Gem::Version
104
- version: '0'
105
- type: :development
106
- prerelease: false
107
- version_requirements: !ruby/object:Gem::Requirement
108
- requirements:
109
- - - ">="
110
- - !ruby/object:Gem::Version
111
- version: '0'
112
- - !ruby/object:Gem::Dependency
113
- name: simplecov
43
+ name: openssl-extensions
114
44
  requirement: !ruby/object:Gem::Requirement
115
45
  requirements:
116
46
  - - ">="
@@ -135,14 +65,13 @@ files:
135
65
  - History.md
136
66
  - LICENSE
137
67
  - README.md
138
- - lib/action_dispatch/middleware/session/dalli_store.rb
139
- - lib/active_support/cache/dalli_store.rb
140
68
  - lib/dalli.rb
141
69
  - lib/dalli/cas/client.rb
142
70
  - lib/dalli/client.rb
143
71
  - lib/dalli/compressor.rb
144
72
  - lib/dalli/options.rb
145
- - lib/dalli/railtie.rb
73
+ - lib/dalli/protocol.rb
74
+ - lib/dalli/protocol/binary.rb
146
75
  - lib/dalli/ring.rb
147
76
  - lib/dalli/server.rb
148
77
  - lib/dalli/socket.rb
@@ -152,9 +81,8 @@ homepage: https://github.com/petergoldstein/dalli
152
81
  licenses:
153
82
  - MIT
154
83
  metadata: {}
155
- post_install_message:
156
- rdoc_options:
157
- - "--charset=UTF-8"
84
+ post_install_message:
85
+ rdoc_options: []
158
86
  require_paths:
159
87
  - lib
160
88
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -168,9 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
96
  - !ruby/object:Gem::Version
169
97
  version: '0'
170
98
  requirements: []
171
- rubyforge_project:
172
- rubygems_version: 2.5.1
173
- signing_key:
99
+ rubygems_version: 3.2.29
100
+ signing_key:
174
101
  specification_version: 4
175
102
  summary: High performance memcached client for Ruby
176
103
  test_files: []