dalli 5.2.5 → 5.2.7

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: 64bd21546442078425398b10e3baa0b75000a4cc8388dcac08c834909bc29731
4
- data.tar.gz: 67568b0a0d030ce81c657cb98a2957f0a6fc12d5ed98f485305e4a93573474f8
3
+ metadata.gz: 7f2cea6074db3fcade9ffdc5277b26cddb9752ee9a4229536e2a883755d4137e
4
+ data.tar.gz: 0f4294f047e670e18a2c97c0c7187e84449096a5e230faf7956547e634c996e9
5
5
  SHA512:
6
- metadata.gz: b069ac0229a977d837ba26755ba279c9eda6b3295bb6c68fabc9baf07ae4c7367cc6acd6d1132f0a5e2913f4b000c8881df0094562b7dd773e34877fab8c21b3
7
- data.tar.gz: 84fbaba58b48b46b7be72fbef38006259ce90268d8eb2ccfa3a1ceeee57a69e9f05472db76d2382d0a8d2e2965e63c363484f351a08bd99b9f9d65e7524d294e
6
+ metadata.gz: af2703898196c9ea24497f5777061d90b6797acb39faec3fc82b6206d2beb02668420d318bb519728b1f72383e66c05af465592ab0726e3703f5944b93a78c32
7
+ data.tar.gz: a59742b1c0c146cd2b36f0d36d55fc94c4491353d364a1c8a3f216452ba293b801d79fb7c34dc3ffa85bbe3aea0d72f82621f67dad3aada23579a5e50048e312
data/CHANGELOG.md CHANGED
@@ -1,9 +1,21 @@
1
1
  Dalli Changelog
2
2
  =====================
3
3
 
4
- Unreleased
4
+ 5.2.7
5
5
  ==========
6
6
 
7
+ Bug fixes:
8
+
9
+ - `Rack::Session::Dalli`'s 401 for a session deleted mid-request called `Hash#slice` on the session options. A Rails session store puts an `ActionDispatch::Request::Session::Options` there, so the 401 raised `NoMethodError` instead of expiring the cookie (#1291, @seballot)
10
+ - Since 5.2.0, on JRuby with TLS, Dalli could wait `socket_timeout` for part of a reply that had already arrived, because jruby-openssl can hold it where neither `IO.select` nor OpenSSL's `pending` reports it. A `get_multi` could take `socket_timeout` longer than it needed to. Dalli now reads on JRuby until the TLS socket would block (#1293)
11
+
12
+ 5.2.6
13
+ ==========
14
+
15
+ Bug fixes:
16
+
17
+ - Raw values in an encoding that isn't ASCII-compatible, such as UTF-16, raised `Encoding::CompatibilityError` from `set`, `add`, `replace`, `append`, `prepend` and `set_multi`, as did a single-server `set_multi` mixing non-ASCII UTF-8 values with binary ones (such as raw Strings of both kinds, or with a JSON serializer, a value containing `é` next to a compressed one). A `p_token` or `l_token` with non-ASCII characters, such as `é`, did the same for any binary value, including every Marshal-serialized one. The error also closed the connection, so in a `quiet` block, or within one server's part of a multi-server `set_multi`, writes buffered before it were silently lost. Values are now sent as their bytes, which is what memcached stores (#1269)
18
+
7
19
  5.2.5
8
20
  ==========
9
21
 
@@ -167,7 +167,7 @@ module Dalli
167
167
  ttl: ttl, mode: mode, quiet: quiet,
168
168
  **routing_token_kwargs(options))
169
169
  end
170
- write("#{req}#{value}#{TERMINATOR}")
170
+ write(request_with_value(req, value))
171
171
  finish_write(quiet)
172
172
  end
173
173
  # rubocop:enable Metrics/ParameterLists
@@ -188,10 +188,20 @@ module Dalli
188
188
  req = RequestFormatter.meta_set(key: key, value: value,
189
189
  cas: cas, ttl: ttl, mode: mode, quiet: quiet?,
190
190
  **routing_token_kwargs(options))
191
- write("#{req}#{value}#{TERMINATOR}")
191
+ write(request_with_value(req, value))
192
192
  finish_write(quiet?)
193
193
  end
194
194
 
195
+ # Ruby refuses to join Strings whose encodings clash, such as a raw
196
+ # UTF-16 value, or a routing token with non-ASCII characters next to a
197
+ # binary value. memcached stores bytes, so join the bytes then.
198
+ # Joining the Strings first keeps the common case free of extra checks.
199
+ def request_with_value(req, value)
200
+ "#{req}#{value}#{TERMINATOR}"
201
+ rescue Encoding::CompatibilityError
202
+ "#{req.b}#{value.b}#{TERMINATOR}"
203
+ end
204
+
195
205
  # Delete Commands
196
206
  #
197
207
  # `options` supports the meta-delete keys :invalidate, :tombstone_ttl and
@@ -120,8 +120,9 @@ module Dalli
120
120
 
121
121
  def multi_meta_set(entries, ttl: nil, p_token: nil, l_token: nil)
122
122
  # Routing tokens apply to every entry in the batch, so the suffix is
123
- # built once rather than per entry.
124
- token_suffix = routing_tokens(p_token: p_token, l_token: l_token)
123
+ # built once rather than per entry. It's binary, like the buffer, so
124
+ # a token with non-ASCII characters doesn't turn the buffer UTF-8.
125
+ token_suffix = routing_tokens(p_token: p_token, l_token: l_token).b
125
126
  ttl = integer_flag(:ttl, ttl) if ttl
126
127
 
127
128
  buffer = ''.b
@@ -136,11 +137,29 @@ module Dalli
136
137
  buffer << ' b' if base64
137
138
  buffer << " F#{bitflags}" if bitflags
138
139
  buffer << " T#{ttl}" if ttl
139
- buffer << ' MS q' << token_suffix << TERMINATOR << value << TERMINATOR
140
+ buffer << ' MS q' << token_suffix << TERMINATOR
141
+ append_bytes(buffer, value)
142
+ buffer << TERMINATOR
140
143
  end
141
144
  buffer << META_NOOP
142
145
  end
143
146
 
147
+ # Values are appended as bytes. One batch can hold UTF-8 and binary
148
+ # values, which String#<< refuses to combine, and a raw value can be in
149
+ # an encoding that isn't ASCII-compatible, such as UTF-16.
150
+ # Don't chain on the result: TruffleRuby's append_as_bytes returns its
151
+ # arguments rather than the buffer.
152
+ if String.method_defined?(:append_as_bytes)
153
+ def append_bytes(buffer, value)
154
+ buffer.append_as_bytes(value)
155
+ end
156
+ else
157
+ def append_bytes(buffer, value)
158
+ buffer << (value.encoding == Encoding::BINARY ? value : value.b)
159
+ end
160
+ end
161
+ private :append_bytes
162
+
144
163
  # Thundering herd protection flag:
145
164
  # - stale (I flag): Instead of deleting the item, mark it as stale. Other clients
146
165
  # using N/R flags will see the X flag and know the item is being regenerated.
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
- def buffered_data?
114
- pending.positive?
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)
data/lib/dalli/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dalli
4
- VERSION = '5.2.5'
4
+ VERSION = '5.2.7'
5
5
 
6
6
  MIN_SUPPORTED_MEMCACHED_VERSION = '1.6.27'
7
7
  end
@@ -184,9 +184,12 @@ module Rack
184
184
  end
185
185
  end
186
186
 
187
+ # Rails stores an ActionDispatch::Request::Session::Options in the env, not a
188
+ # Hash. It implements [] (and to_hash) but not Hash#slice, so slicing it
189
+ # raised NoMethodError instead of returning this 401.
187
190
  def missing_session_response(env)
188
191
  options = env[Rack::RACK_SESSION_OPTIONS] || @default_options
189
- cookie = Rack::Utils.delete_set_cookie_header(key, options.slice(:path, :domain))
192
+ cookie = Rack::Utils.delete_set_cookie_header(key, { path: options[:path], domain: options[:domain] })
190
193
  [401, { 'content-type' => 'text/plain', 'set-cookie' => cookie }, ['Wrong session ID']]
191
194
  end
192
195
 
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.2.5
4
+ version: 5.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein