dalli 2.7.0 → 3.0.4

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/server.rb CHANGED
@@ -1,692 +1,6 @@
1
- require 'socket'
2
- require 'timeout'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Dalli
5
- class Server
6
- attr_accessor :hostname
7
- attr_accessor :port
8
- attr_accessor :weight
9
- attr_accessor :options
10
- attr_reader :sock
11
-
12
- DEFAULTS = {
13
- # seconds between trying to contact a remote server
14
- :down_retry_delay => 1,
15
- # connect/read/write timeout for socket operations
16
- :socket_timeout => 0.5,
17
- # times a socket operation may fail before considering the server dead
18
- :socket_max_failures => 2,
19
- # amount of time to sleep between retries when a failure occurs
20
- :socket_failure_delay => 0.01,
21
- # max size of value in bytes (default is 1 MB, can be overriden with "memcached -I <size>")
22
- :value_max_bytes => 1024 * 1024,
23
- :compressor => Compressor,
24
- # min byte size to attempt compression
25
- :compression_min_size => 1024,
26
- # max byte size for compression
27
- :compression_max_size => false,
28
- :serializer => Marshal,
29
- :username => nil,
30
- :password => nil,
31
- :keepalive => true
32
- }
33
-
34
- def initialize(attribs, options = {})
35
- (@hostname, @port, @weight) = attribs.split(':')
36
- @port ||= 11211
37
- @port = Integer(@port)
38
- @weight ||= 1
39
- @weight = Integer(@weight)
40
- @fail_count = 0
41
- @down_at = nil
42
- @last_down_at = nil
43
- @options = DEFAULTS.merge(options)
44
- @sock = nil
45
- @msg = nil
46
- @error = nil
47
- @pid = nil
48
- @inprogress = nil
49
- end
50
-
51
- def name
52
- "#{@hostname}:#{@port}"
53
- end
54
-
55
- # Chokepoint method for instrumentation
56
- def request(op, *args)
57
- verify_state
58
- raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}. If you are sure it is running, ensure memcached version is > 1.4." unless alive?
59
- begin
60
- send(op, *args)
61
- rescue Dalli::NetworkError
62
- raise
63
- rescue Dalli::MarshalError => ex
64
- Dalli.logger.error "Marshalling error for key '#{args.first}': #{ex.message}"
65
- Dalli.logger.error "You are trying to cache a Ruby object which cannot be serialized to memcached."
66
- Dalli.logger.error ex.backtrace.join("\n\t")
67
- false
68
- rescue Dalli::DalliError
69
- raise
70
- rescue => ex
71
- Dalli.logger.error "Unexpected exception in Dalli: #{ex.class.name}: #{ex.message}"
72
- Dalli.logger.error "This is a bug in Dalli, please enter an issue in Github if it does not already exist."
73
- Dalli.logger.error ex.backtrace.join("\n\t")
74
- down!
75
- end
76
- end
77
-
78
- def alive?
79
- return true if @sock
80
-
81
- if @last_down_at && @last_down_at + options[:down_retry_delay] >= Time.now
82
- time = @last_down_at + options[:down_retry_delay] - Time.now
83
- Dalli.logger.debug { "down_retry_delay not reached for #{hostname}:#{port} (%.3f seconds left)" % time }
84
- return false
85
- end
86
-
87
- connect
88
- !!@sock
89
- rescue Dalli::NetworkError
90
- false
91
- end
92
-
93
- def close
94
- return unless @sock
95
- @sock.close rescue nil
96
- @sock = nil
97
- @pid = nil
98
- @inprogress = false
99
- end
100
-
101
- def lock!
102
- end
103
-
104
- def unlock!
105
- end
106
-
107
- def serializer
108
- @options[:serializer]
109
- end
110
-
111
- def compressor
112
- @options[:compressor]
113
- end
114
-
115
- # Start reading key/value pairs from this connection. This is usually called
116
- # after a series of GETKQ commands. A NOOP is sent, and the server begins
117
- # flushing responses for kv pairs that were found.
118
- #
119
- # Returns nothing.
120
- def multi_response_start
121
- verify_state
122
- write_noop
123
- @multi_buffer = ''
124
- @position = 0
125
- @inprogress = true
126
- end
127
-
128
- # Did the last call to #multi_response_start complete successfully?
129
- def multi_response_completed?
130
- @multi_buffer.nil?
131
- end
132
-
133
- # Attempt to receive and parse as many key/value pairs as possible
134
- # from this server. After #multi_response_start, this should be invoked
135
- # repeatedly whenever this server's socket is readable until
136
- # #multi_response_completed?.
137
- #
138
- # Returns a Hash of kv pairs received.
139
- def multi_response_nonblock
140
- raise 'multi_response has completed' if @multi_buffer.nil?
141
-
142
- @multi_buffer << @sock.read_available
143
- buf = @multi_buffer
144
- pos = @position
145
- values = {}
146
-
147
- while buf.bytesize - pos >= 24
148
- header = buf.slice(pos, 24)
149
- (key_length, _, body_length, cas) = header.unpack(KV_HEADER)
150
-
151
- if key_length == 0
152
- # all done!
153
- @multi_buffer = nil
154
- @position = nil
155
- @inprogress = false
156
- break
157
-
158
- elsif buf.bytesize - pos >= 24 + body_length
159
- flags = buf.slice(pos + 24, 4).unpack('N')[0]
160
- key = buf.slice(pos + 24 + 4, key_length)
161
- value = buf.slice(pos + 24 + 4 + key_length, body_length - key_length - 4) if body_length - key_length - 4 > 0
162
-
163
- pos = pos + 24 + body_length
164
-
165
- begin
166
- values[key] = [deserialize(value, flags), cas]
167
- rescue DalliError
168
- end
169
-
170
- else
171
- # not enough data yet, wait for more
172
- break
173
- end
174
- end
175
- @position = pos
176
-
177
- values
178
- rescue SystemCallError, Timeout::Error, EOFError => e
179
- failure!(e)
180
- end
181
-
182
- # Abort an earlier #multi_response_start. Used to signal an external
183
- # timeout. The underlying socket is disconnected, and the exception is
184
- # swallowed.
185
- #
186
- # Returns nothing.
187
- def multi_response_abort
188
- @multi_buffer = nil
189
- @position = nil
190
- @inprogress = false
191
- failure!(RuntimeError.new('External timeout'))
192
- rescue NetworkError
193
- true
194
- end
195
-
196
- # NOTE: Additional public methods should be overridden in Dalli::Threadsafe
197
-
198
- private
199
-
200
- def verify_state
201
- failure!(RuntimeError.new('Already writing to socket')) if @inprogress
202
- failure!(RuntimeError.new('Cannot share client between multiple processes')) if @pid && @pid != Process.pid
203
- end
204
-
205
- def failure!(exception)
206
- message = "#{hostname}:#{port} failed (count: #{@fail_count}) #{exception.class}: #{exception.message}"
207
- Dalli.logger.info { message }
208
-
209
- @fail_count += 1
210
- if @fail_count >= options[:socket_max_failures]
211
- down!
212
- else
213
- close
214
- sleep(options[:socket_failure_delay]) if options[:socket_failure_delay]
215
- raise Dalli::NetworkError, "Socket operation failed, retrying..."
216
- end
217
- end
218
-
219
- def down!
220
- close
221
-
222
- @last_down_at = Time.now
223
-
224
- if @down_at
225
- time = Time.now - @down_at
226
- Dalli.logger.debug { "#{hostname}:#{port} is still down (for %.3f seconds now)" % time }
227
- else
228
- @down_at = @last_down_at
229
- Dalli.logger.warn { "#{hostname}:#{port} is down" }
230
- end
231
-
232
- @error = $! && $!.class.name
233
- @msg = @msg || ($! && $!.message && !$!.message.empty? && $!.message)
234
- raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}"
235
- end
236
-
237
- def up!
238
- if @down_at
239
- time = Time.now - @down_at
240
- Dalli.logger.warn { "#{hostname}:#{port} is back (downtime was %.3f seconds)" % time }
241
- end
242
-
243
- @fail_count = 0
244
- @down_at = nil
245
- @last_down_at = nil
246
- @msg = nil
247
- @error = nil
248
- end
249
-
250
- def multi?
251
- Thread.current[:dalli_multi]
252
- end
253
-
254
- def get(key)
255
- req = [REQUEST, OPCODES[:get], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:get])
256
- write(req)
257
- generic_response(true)
258
- end
259
-
260
- def send_multiget(keys)
261
- req = ""
262
- keys.each do |key|
263
- req << [REQUEST, OPCODES[:getkq], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:getkq])
264
- end
265
- # Could send noop here instead of in multi_response_start
266
- write(req)
267
- end
268
-
269
- def set(key, value, ttl, cas, options)
270
- (value, flags) = serialize(key, value, options)
271
-
272
- guard_max_value(key, value) do
273
- req = [REQUEST, OPCODES[multi? ? :setq : :set], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:set])
274
- write(req)
275
- cas_response unless multi?
276
- end
277
- end
278
-
279
- def add(key, value, ttl, options)
280
- (value, flags) = serialize(key, value, options)
281
-
282
- guard_max_value(key, value) do
283
- req = [REQUEST, OPCODES[multi? ? :addq : :add], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:add])
284
- write(req)
285
- cas_response unless multi?
286
- end
287
- end
288
-
289
- def replace(key, value, ttl, cas, options)
290
- (value, flags) = serialize(key, value, options)
291
-
292
- guard_max_value(key, value) do
293
- req = [REQUEST, OPCODES[multi? ? :replaceq : :replace], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:replace])
294
- write(req)
295
- cas_response unless multi?
296
- end
297
- end
298
-
299
- def delete(key, cas)
300
- req = [REQUEST, OPCODES[multi? ? :deleteq : :delete], key.bytesize, 0, 0, 0, key.bytesize, 0, cas, key].pack(FORMAT[:delete])
301
- write(req)
302
- generic_response unless multi?
303
- end
304
-
305
- def flush(ttl)
306
- req = [REQUEST, OPCODES[:flush], 0, 4, 0, 0, 4, 0, 0, 0].pack(FORMAT[:flush])
307
- write(req)
308
- generic_response
309
- end
310
-
311
- def decr(key, count, ttl, default)
312
- expiry = default ? ttl : 0xFFFFFFFF
313
- default ||= 0
314
- (h, l) = split(count)
315
- (dh, dl) = split(default)
316
- req = [REQUEST, OPCODES[:decr], key.bytesize, 20, 0, 0, key.bytesize + 20, 0, 0, h, l, dh, dl, expiry, key].pack(FORMAT[:decr])
317
- write(req)
318
- body = generic_response
319
- body ? longlong(*body.unpack('NN')) : body
320
- end
321
-
322
- def incr(key, count, ttl, default)
323
- expiry = default ? ttl : 0xFFFFFFFF
324
- default ||= 0
325
- (h, l) = split(count)
326
- (dh, dl) = split(default)
327
- req = [REQUEST, OPCODES[:incr], key.bytesize, 20, 0, 0, key.bytesize + 20, 0, 0, h, l, dh, dl, expiry, key].pack(FORMAT[:incr])
328
- write(req)
329
- body = generic_response
330
- body ? longlong(*body.unpack('NN')) : body
331
- end
332
-
333
- def write_noop
334
- req = [REQUEST, OPCODES[:noop], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
335
- write(req)
336
- end
337
-
338
- # Noop is a keepalive operation but also used to demarcate the end of a set of pipelined commands.
339
- # We need to read all the responses at once.
340
- def noop
341
- write_noop
342
- multi_response
343
- end
344
-
345
- def append(key, value)
346
- req = [REQUEST, OPCODES[:append], key.bytesize, 0, 0, 0, value.bytesize + key.bytesize, 0, 0, key, value].pack(FORMAT[:append])
347
- write(req)
348
- generic_response
349
- end
350
-
351
- def prepend(key, value)
352
- req = [REQUEST, OPCODES[:prepend], key.bytesize, 0, 0, 0, value.bytesize + key.bytesize, 0, 0, key, value].pack(FORMAT[:prepend])
353
- write(req)
354
- generic_response
355
- end
356
-
357
- def stats(info='')
358
- req = [REQUEST, OPCODES[:stat], info.bytesize, 0, 0, 0, info.bytesize, 0, 0, info].pack(FORMAT[:stat])
359
- write(req)
360
- keyvalue_response
361
- end
362
-
363
- def reset_stats
364
- req = [REQUEST, OPCODES[:stat], 'reset'.bytesize, 0, 0, 0, 'reset'.bytesize, 0, 0, 'reset'].pack(FORMAT[:stat])
365
- write(req)
366
- generic_response
367
- end
368
-
369
- def cas(key)
370
- req = [REQUEST, OPCODES[:get], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:get])
371
- write(req)
372
- data_cas_response
373
- end
374
-
375
- def version
376
- req = [REQUEST, OPCODES[:version], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
377
- write(req)
378
- generic_response
379
- end
380
-
381
- def touch(key, ttl)
382
- req = [REQUEST, OPCODES[:touch], key.bytesize, 4, 0, 0, key.bytesize + 4, 0, 0, ttl, key].pack(FORMAT[:touch])
383
- write(req)
384
- generic_response
385
- end
386
-
387
- # http://www.hjp.at/zettel/m/memcached_flags.rxml
388
- # Looks like most clients use bit 0 to indicate native language serialization
389
- # and bit 1 to indicate gzip compression.
390
- FLAG_SERIALIZED = 0x1
391
- FLAG_COMPRESSED = 0x2
392
-
393
- def serialize(key, value, options=nil)
394
- marshalled = false
395
- value = unless options && options[:raw]
396
- marshalled = true
397
- begin
398
- self.serializer.dump(value)
399
- rescue => ex
400
- # Marshalling can throw several different types of generic Ruby exceptions.
401
- # Convert to a specific exception so we can special case it higher up the stack.
402
- exc = Dalli::MarshalError.new(ex.message)
403
- exc.set_backtrace ex.backtrace
404
- raise exc
405
- end
406
- else
407
- value.to_s
408
- end
409
- compressed = false
410
- if @options[:compress] && value.bytesize >= @options[:compression_min_size] &&
411
- (!@options[:compression_max_size] || value.bytesize <= @options[:compression_max_size])
412
- value = self.compressor.compress(value)
413
- compressed = true
414
- end
415
-
416
- flags = 0
417
- flags |= FLAG_COMPRESSED if compressed
418
- flags |= FLAG_SERIALIZED if marshalled
419
- [value, flags]
420
- end
421
-
422
- def deserialize(value, flags)
423
- value = self.compressor.decompress(value) if (flags & FLAG_COMPRESSED) != 0
424
- value = self.serializer.load(value) if (flags & FLAG_SERIALIZED) != 0
425
- value
426
- rescue TypeError
427
- raise if $!.message !~ /needs to have method `_load'|exception class\/object expected|instance of IO needed|incompatible marshal file format/
428
- raise UnmarshalError, "Unable to unmarshal value: #{$!.message}"
429
- rescue ArgumentError
430
- raise if $!.message !~ /undefined class|marshal data too short/
431
- raise UnmarshalError, "Unable to unmarshal value: #{$!.message}"
432
- rescue Zlib::Error
433
- raise UnmarshalError, "Unable to uncompress value: #{$!.message}"
434
- end
435
-
436
- def data_cas_response
437
- header = read(24)
438
- raise Dalli::NetworkError, 'No response' if !header
439
- (extras, _, status, count, _, cas) = header.unpack(CAS_HEADER)
440
- data = read(count) if count > 0
441
- if status == 1
442
- nil
443
- elsif status != 0
444
- raise Dalli::DalliError, "Response error #{status}: #{RESPONSE_CODES[status]}"
445
- elsif data
446
- flags = data[0...extras].unpack('N')[0]
447
- value = data[extras..-1]
448
- data = deserialize(value, flags)
449
- end
450
- [data, cas]
451
- end
452
-
453
- CAS_HEADER = '@4CCnNNQ'
454
- NORMAL_HEADER = '@4CCnN'
455
- KV_HEADER = '@2n@6nN@16Q'
456
-
457
- def guard_max_value(key, value)
458
- if value.bytesize <= @options[:value_max_bytes]
459
- yield
460
- else
461
- Dalli.logger.warn "Value for #{key} over max size: #{@options[:value_max_bytes]} <= #{value.bytesize}"
462
- false
463
- end
464
- end
465
-
466
- def generic_response(unpack=false)
467
- header = read(24)
468
- raise Dalli::NetworkError, 'No response' if !header
469
- (extras, _, status, count) = header.unpack(NORMAL_HEADER)
470
- data = read(count) if count > 0
471
- if status == 1
472
- nil
473
- elsif status == 2 || status == 5
474
- false # Not stored, normal status for add operation
475
- elsif status != 0
476
- raise Dalli::DalliError, "Response error #{status}: #{RESPONSE_CODES[status]}"
477
- elsif data
478
- flags = data[0...extras].unpack('N')[0]
479
- value = data[extras..-1]
480
- unpack ? deserialize(value, flags) : value
481
- else
482
- true
483
- end
484
- end
485
-
486
- def cas_response
487
- header = read(24)
488
- raise Dalli::NetworkError, 'No response' if !header
489
- (_, _, status, count, _, cas) = header.unpack(CAS_HEADER)
490
- read(count) if count > 0 # this is potential data that we don't care about
491
- if status == 1
492
- nil
493
- elsif status == 2 || status == 5
494
- false # Not stored, normal status for add operation
495
- elsif status != 0
496
- raise Dalli::DalliError, "Response error #{status}: #{RESPONSE_CODES[status]}"
497
- else
498
- cas
499
- end
500
- end
501
-
502
- def keyvalue_response
503
- hash = {}
504
- loop do
505
- header = read(24)
506
- raise Dalli::NetworkError, 'No response' if !header
507
- (key_length, _, body_length, _) = header.unpack(KV_HEADER)
508
- return hash if key_length == 0
509
- key = read(key_length)
510
- value = read(body_length - key_length) if body_length - key_length > 0
511
- hash[key] = value
512
- end
513
- end
514
-
515
- def multi_response
516
- hash = {}
517
- loop do
518
- header = read(24)
519
- raise Dalli::NetworkError, 'No response' if !header
520
- (key_length, _, body_length, _) = header.unpack(KV_HEADER)
521
- return hash if key_length == 0
522
- flags = read(4).unpack('N')[0]
523
- key = read(key_length)
524
- value = read(body_length - key_length - 4) if body_length - key_length - 4 > 0
525
- hash[key] = deserialize(value, flags)
526
- end
527
- end
528
-
529
- def write(bytes)
530
- begin
531
- @inprogress = true
532
- result = @sock.write(bytes)
533
- @inprogress = false
534
- result
535
- rescue SystemCallError, Timeout::Error => e
536
- failure!(e)
537
- end
538
- end
539
-
540
- def read(count)
541
- begin
542
- @inprogress = true
543
- data = @sock.readfull(count)
544
- @inprogress = false
545
- data
546
- rescue SystemCallError, Timeout::Error, EOFError => e
547
- failure!(e)
548
- end
549
- end
550
-
551
- def connect
552
- Dalli.logger.debug { "Dalli::Server#connect #{hostname}:#{port}" }
553
-
554
- begin
555
- @pid = Process.pid
556
- @sock = KSocket.open(hostname, port, self, options)
557
- @version = version # trigger actual connect
558
- sasl_authentication if need_auth?
559
- up!
560
- rescue Dalli::DalliError # SASL auth failure
561
- raise
562
- rescue SystemCallError, Timeout::Error, EOFError, SocketError => e
563
- # SocketError = DNS resolution failure
564
- failure!(e)
565
- end
566
- end
567
-
568
- def split(n)
569
- [n >> 32, 0xFFFFFFFF & n]
570
- end
571
-
572
- def longlong(a, b)
573
- (a << 32) | b
574
- end
575
-
576
- REQUEST = 0x80
577
- RESPONSE = 0x81
578
-
579
- RESPONSE_CODES = {
580
- 0 => 'No error',
581
- 1 => 'Key not found',
582
- 2 => 'Key exists',
583
- 3 => 'Value too large',
584
- 4 => 'Invalid arguments',
585
- 5 => 'Item not stored',
586
- 6 => 'Incr/decr on a non-numeric value',
587
- 0x20 => 'Authentication required',
588
- 0x81 => 'Unknown command',
589
- 0x82 => 'Out of memory',
590
- }
591
-
592
- OPCODES = {
593
- :get => 0x00,
594
- :set => 0x01,
595
- :add => 0x02,
596
- :replace => 0x03,
597
- :delete => 0x04,
598
- :incr => 0x05,
599
- :decr => 0x06,
600
- :flush => 0x08,
601
- :noop => 0x0A,
602
- :version => 0x0B,
603
- :getkq => 0x0D,
604
- :append => 0x0E,
605
- :prepend => 0x0F,
606
- :stat => 0x10,
607
- :setq => 0x11,
608
- :addq => 0x12,
609
- :replaceq => 0x13,
610
- :deleteq => 0x14,
611
- :incrq => 0x15,
612
- :decrq => 0x16,
613
- :auth_negotiation => 0x20,
614
- :auth_request => 0x21,
615
- :auth_continue => 0x22,
616
- :touch => 0x1C,
617
- }
618
-
619
- HEADER = "CCnCCnNNQ"
620
- OP_FORMAT = {
621
- :get => 'a*',
622
- :set => 'NNa*a*',
623
- :add => 'NNa*a*',
624
- :replace => 'NNa*a*',
625
- :delete => 'a*',
626
- :incr => 'NNNNNa*',
627
- :decr => 'NNNNNa*',
628
- :flush => 'N',
629
- :noop => '',
630
- :getkq => 'a*',
631
- :version => '',
632
- :stat => 'a*',
633
- :append => 'a*a*',
634
- :prepend => 'a*a*',
635
- :auth_request => 'a*a*',
636
- :auth_continue => 'a*a*',
637
- :touch => 'Na*',
638
- }
639
- FORMAT = OP_FORMAT.inject({}) { |memo, (k, v)| memo[k] = HEADER + v; memo }
640
-
641
-
642
- #######
643
- # SASL authentication support for NorthScale
644
- #######
645
-
646
- def need_auth?
647
- @options[:username] || ENV['MEMCACHE_USERNAME']
648
- end
649
-
650
- def username
651
- @options[:username] || ENV['MEMCACHE_USERNAME']
652
- end
653
-
654
- def password
655
- @options[:password] || ENV['MEMCACHE_PASSWORD']
656
- end
657
-
658
- def sasl_authentication
659
- Dalli.logger.info { "Dalli/SASL authenticating as #{username}" }
660
-
661
- # negotiate
662
- req = [REQUEST, OPCODES[:auth_negotiation], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
663
- write(req)
664
- header = read(24)
665
- raise Dalli::NetworkError, 'No response' if !header
666
- (extras, type, status, count) = header.unpack(NORMAL_HEADER)
667
- raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
668
- content = read(count)
669
- return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
670
- mechanisms = content.split(' ')
671
- raise NotImplementedError, "Dalli only supports the PLAIN authentication mechanism" if !mechanisms.include?('PLAIN')
672
-
673
- # request
674
- mechanism = 'PLAIN'
675
- msg = "\x0#{username}\x0#{password}"
676
- req = [REQUEST, OPCODES[:auth_request], mechanism.bytesize, 0, 0, 0, mechanism.bytesize + msg.bytesize, 0, 0, mechanism, msg].pack(FORMAT[:auth_request])
677
- write(req)
678
-
679
- header = read(24)
680
- raise Dalli::NetworkError, 'No response' if !header
681
- (extras, type, status, count) = header.unpack(NORMAL_HEADER)
682
- raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
683
- content = read(count)
684
- return Dalli.logger.info("Dalli/SASL: #{content}") if status == 0
685
-
686
- raise Dalli::DalliError, "Error authenticating: #{status}" unless status == 0x21
687
- raise NotImplementedError, "No two-step authentication mechanisms supported"
688
- # (step, msg) = sasl.receive('challenge', content)
689
- # raise Dalli::NetworkError, "Authentication failed" if sasl.failed? || step != 'response'
690
- end
691
- end
4
+ warn "Dalli::Server is deprecated, use Dalli::Protocol::Binary instead"
5
+ Server = Protocol::Binary
692
6
  end