dalli 4.0.0 → 4.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86e39b2869b7c916472e88e6cbbfb6aa1f6533aa35c9e1f1c704cffcbc680f8a
4
- data.tar.gz: bf2e56610c6bae187d561ccf09105e6f5b4a29eed1308d089745ef22cf5b673b
3
+ metadata.gz: 5494b3c5f7ce6c73cb061511ab96c7dafc0ed1f5d10a865310a7adc79c8587c9
4
+ data.tar.gz: 1326cd38aad6ba7c7c0c65c2b0305b4c4c566e3a0050df1f8ccee6aa42ee2bc7
5
5
  SHA512:
6
- metadata.gz: e2faab814abb4b25a53d87048f7e4e81e49c609bd64725cb18607670110da3d5b8038544598c44575831b9038500f9d8220a56ec28c16b9517a21d442212d12b
7
- data.tar.gz: 3e5a7c326c908d72d2d8ce2211b27f67acebbbf97c279c5faf3ba2914ec7409df7f259a9082d82903cc9ae6b6716a137994aba849321546b8d3d3a35f8f7f5af
6
+ metadata.gz: 5affa5731ead45a7c352628c4c3c471b1fc5c7499e9e63e58278e2c59c309e90fd550774a8808de78ff9daaa53913503810ded029d0879f3a6408f19a4bf67f2
7
+ data.tar.gz: 244b909ac405e2f8757b3bfa3fdcea3fb17615f6d329527f63c3392a4921dd8298fa9d6eeaab368bd80112413c593d94f305062732fb28204072526b84eba3d4
data/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  Dalli Changelog
2
2
  =====================
3
3
 
4
+ 4.0.1
5
+ ==========
6
+
7
+ - Add `:raw` client option to skip serialization entirely, returning raw byte strings
8
+ - Handle `OpenSSL::SSL::SSLError` in connection manager
9
+
4
10
  4.0.0
5
11
  ==========
6
12
 
data/Gemfile CHANGED
@@ -5,11 +5,18 @@ source 'https://rubygems.org'
5
5
  gemspec
6
6
 
7
7
  group :development, :test do
8
+ gem 'benchmark'
8
9
  gem 'cgi'
9
10
  gem 'connection_pool'
10
11
  gem 'debug' unless RUBY_PLATFORM == 'java'
11
- gem 'minitest', '~> 5'
12
- gem 'rack', '~> 2.0', '>= 2.2.0'
12
+ if RUBY_VERSION >= '3.2'
13
+ gem 'minitest', '~> 6'
14
+ gem 'minitest-mock'
15
+ else
16
+ gem 'minitest', '~> 5'
17
+ end
18
+ gem 'rack', '~> 3'
19
+ gem 'rack-session'
13
20
  gem 'rake', '~> 13.0'
14
21
  gem 'rubocop'
15
22
  gem 'rubocop-minitest'
data/README.md CHANGED
@@ -14,6 +14,32 @@ Dalli supports:
14
14
 
15
15
  The name is a variant of Salvador Dali for his famous painting [The Persistence of Memory](http://en.wikipedia.org/wiki/The_Persistence_of_Memory).
16
16
 
17
+ ## Requirements
18
+
19
+ * Ruby 3.1 or later
20
+ * memcached 1.4 or later (1.6+ recommended for meta protocol support)
21
+
22
+ ## Protocol Options
23
+
24
+ Dalli supports two protocols for communicating with memcached:
25
+
26
+ * `:binary` (default) - Works with all memcached versions, supports SASL authentication
27
+ * `:meta` - Requires memcached 1.6+, better performance for some operations, no authentication support
28
+
29
+ ```ruby
30
+ Dalli::Client.new('localhost:11211', protocol: :meta)
31
+ ```
32
+
33
+ ## Security Note
34
+
35
+ By default, Dalli uses Ruby's Marshal for serialization. Deserializing untrusted data with Marshal can lead to remote code execution. If you cache user-controlled data, consider using a safer serializer:
36
+
37
+ ```ruby
38
+ Dalli::Client.new('localhost:11211', serializer: JSON)
39
+ ```
40
+
41
+ See the [4.0-Upgrade.md](4.0-Upgrade.md) guide for more information.
42
+
17
43
  ![Persistence of Memory](https://upload.wikimedia.org/wikipedia/en/d/dd/The_Persistence_of_Memory.jpg)
18
44
 
19
45
 
data/lib/dalli/client.rb CHANGED
@@ -41,6 +41,11 @@ module Dalli
41
41
  # - :compressor - defaults to Dalli::Compressor, a Zlib-based implementation
42
42
  # - :cache_nils - defaults to false, if true Dalli will not treat cached nil values as 'not found' for
43
43
  # #fetch operations.
44
+ # - :raw - If set, disables serialization and compression entirely at the client level.
45
+ # Only String values are supported. This is useful when the caller handles its own
46
+ # serialization (e.g., Rails' ActiveSupport::Cache). Note: this is different from
47
+ # the per-request :raw option which converts values to strings but still uses the
48
+ # serialization pipeline.
44
49
  # - :digest_class - defaults to Digest::MD5, allows you to pass in an object that responds to the hexdigest method,
45
50
  # useful for injecting a FIPS compliant hash object.
46
51
  # - :protocol - one of either :binary or :meta, defaulting to :binary. This sets the protocol that Dalli uses
@@ -23,7 +23,7 @@ module Dalli
23
23
  def initialize(attribs, client_options = {})
24
24
  hostname, port, socket_type, @weight, user_creds = ServerConfigParser.parse(attribs)
25
25
  @options = client_options.merge(user_creds)
26
- @value_marshaller = ValueMarshaller.new(@options)
26
+ @value_marshaller = client_options[:raw] ? StringMarshaller.new(@options) : ValueMarshaller.new(@options)
27
27
  @connection_manager = ConnectionManager.new(hostname, port, socket_type, @options)
28
28
  end
29
29
 
@@ -106,7 +106,7 @@ module Dalli
106
106
  end
107
107
 
108
108
  values
109
- rescue SystemCallError, *TIMEOUT_ERRORS, EOFError => e
109
+ rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS, EOFError => e
110
110
  @connection_manager.error_on_request!(e)
111
111
  end
112
112
 
@@ -150,19 +150,19 @@ module Dalli
150
150
  data = @sock.gets("\r\n")
151
151
  error_on_request!('EOF in read_line') if data.nil?
152
152
  data
153
- rescue SystemCallError, *TIMEOUT_ERRORS, EOFError => e
153
+ rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS, EOFError => e
154
154
  error_on_request!(e)
155
155
  end
156
156
 
157
157
  def read(count)
158
158
  @sock.readfull(count)
159
- rescue SystemCallError, *TIMEOUT_ERRORS, EOFError => e
159
+ rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS, EOFError => e
160
160
  error_on_request!(e)
161
161
  end
162
162
 
163
163
  def write(bytes)
164
164
  @sock.write(bytes)
165
- rescue SystemCallError, *TIMEOUT_ERRORS => e
165
+ rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS => e
166
166
  error_on_request!(e)
167
167
  end
168
168
 
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dalli
4
+ module Protocol
5
+ ##
6
+ # Dalli::Protocol::StringMarshaller is a pass-through marshaller for use with
7
+ # the :raw client option. It bypasses serialization and compression entirely,
8
+ # expecting values to already be strings (e.g., pre-serialized by Rails'
9
+ # ActiveSupport::Cache). It still enforces the maximum value size limit.
10
+ ##
11
+ class StringMarshaller
12
+ DEFAULTS = {
13
+ # max size of value in bytes (default is 1 MB, can be overriden with "memcached -I <size>")
14
+ value_max_bytes: 1024 * 1024
15
+ }.freeze
16
+
17
+ attr_reader :value_max_bytes
18
+
19
+ def initialize(client_options)
20
+ @value_max_bytes = client_options.fetch(:value_max_bytes) do
21
+ DEFAULTS.fetch(:value_max_bytes)
22
+ end.to_i
23
+ end
24
+
25
+ def store(key, value, _options = nil)
26
+ raise MarshalError, "Dalli in :raw mode only supports strings, got: #{value.class}" unless value.is_a?(String)
27
+
28
+ error_if_over_max_value_bytes(key, value)
29
+ [value, 0]
30
+ end
31
+
32
+ def retrieve(value, _flags)
33
+ value
34
+ end
35
+
36
+ # Interface compatibility methods - these return nil since
37
+ # StringMarshaller bypasses serialization and compression entirely.
38
+
39
+ def serializer
40
+ nil
41
+ end
42
+
43
+ def compressor
44
+ nil
45
+ end
46
+
47
+ def compression_min_size
48
+ nil
49
+ end
50
+
51
+ def compress_by_default?
52
+ false
53
+ end
54
+
55
+ private
56
+
57
+ def error_if_over_max_value_bytes(key, value)
58
+ return if value.bytesize <= value_max_bytes
59
+
60
+ message = "Value for #{key} over max size: #{value_max_bytes} <= #{value.bytesize}"
61
+ raise Dalli::ValueOverMaxSize, message
62
+ end
63
+ end
64
+ end
65
+ end
@@ -15,5 +15,15 @@ module Dalli
15
15
  else
16
16
  [Timeout::Error]
17
17
  end
18
+
19
+ # SSL errors that occur during read/write operations (not during initial
20
+ # handshake) should trigger reconnection. These indicate transient network
21
+ # issues, not configuration problems.
22
+ SSL_ERRORS =
23
+ if defined?(OpenSSL::SSL::SSLError)
24
+ [OpenSSL::SSL::SSLError]
25
+ else
26
+ []
27
+ end
18
28
  end
19
29
  end
data/lib/dalli/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dalli
4
- VERSION = '4.0.0'
4
+ VERSION = '4.0.1'
5
5
 
6
6
  MIN_SUPPORTED_MEMCACHED_VERSION = '1.4'
7
7
  end
data/lib/dalli.rb CHANGED
@@ -72,6 +72,7 @@ require_relative 'dalli/protocol/server_config_parser'
72
72
  require_relative 'dalli/protocol/ttl_sanitizer'
73
73
  require_relative 'dalli/protocol/value_compressor'
74
74
  require_relative 'dalli/protocol/value_marshaller'
75
+ require_relative 'dalli/protocol/string_marshaller'
75
76
  require_relative 'dalli/protocol/value_serializer'
76
77
  require_relative 'dalli/servers_arg_normalizer'
77
78
  require_relative 'dalli/socket'
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: 4.0.0
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
@@ -58,6 +58,7 @@ files:
58
58
  - lib/dalli/protocol/meta/response_processor.rb
59
59
  - lib/dalli/protocol/response_buffer.rb
60
60
  - lib/dalli/protocol/server_config_parser.rb
61
+ - lib/dalli/protocol/string_marshaller.rb
61
62
  - lib/dalli/protocol/ttl_sanitizer.rb
62
63
  - lib/dalli/protocol/value_compressor.rb
63
64
  - lib/dalli/protocol/value_marshaller.rb
@@ -88,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
89
  - !ruby/object:Gem::Version
89
90
  version: '0'
90
91
  requirements: []
91
- rubygems_version: 4.0.3
92
+ rubygems_version: 4.0.4
92
93
  specification_version: 4
93
94
  summary: High performance memcached client for Ruby
94
95
  test_files: []