class Dalli::Protocol::Meta::ResponseProcessor

Class that encapsulates logic for processing meta protocol responses from memcached. Includes logic for pulling data from an IO source and parsing into local values. Handles errors on unexpected values.

Constants

EN
END_TOKEN
EX
HD
MN
NF
NS
OK
RESET
STAT
VA
VERSION

Public Class Methods

new(io_source, value_marshaller) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 25
def initialize(io_source, value_marshaller)
  @io_source = io_source
  @value_marshaller = value_marshaller
end

Public Instance Methods

bitflags_from_tokens(tokens) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 175
def bitflags_from_tokens(tokens)
  value_from_tokens(tokens, 'f')&.to_i
end
body_len_from_tokens(tokens) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 189
def body_len_from_tokens(tokens)
  value_from_tokens(tokens, 's')&.to_i
end
cas_from_tokens(tokens) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 179
def cas_from_tokens(tokens)
  value_from_tokens(tokens, 'c')&.to_i
end
consume_all_responses_until_mn() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 107
def consume_all_responses_until_mn
  tokens = next_line_to_tokens

  tokens = next_line_to_tokens while tokens.first != MN
  true
end
contains_header?(buf) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 160
def contains_header?(buf)
  buf.include?(TERMINATOR)
end
decr_incr() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 72
def decr_incr
  tokens = error_on_unexpected!([VA, NF, NS, EX])
  return false if [NS, EX].include?(tokens.first)
  return nil if tokens.first == NF

  read_line.to_i
end
error_on_unexpected!(expected_codes) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 168
def error_on_unexpected!(expected_codes)
  tokens = next_line_to_tokens
  raise Dalli::DalliError, "Response error: #{tokens.first}" unless expected_codes.include?(tokens.first)

  tokens
end
flush() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 90
def flush
  error_on_unexpected!([OK])

  true
end
full_response_from_buffer(tokens, body, resp_size) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 122
def full_response_from_buffer(tokens, body, resp_size)
  value = @value_marshaller.retrieve(body, bitflags_from_tokens(tokens))
  [resp_size, tokens.first == VA, cas_from_tokens(tokens), key_from_tokens(tokens), value]
end
getk_response_from_buffer(buf) click to toggle source

This method returns an array of values used in a pipelined getk process. The first value is the number of bytes by which to advance the pointer in the buffer. If the complete response is found in the buffer, this will be the response size. Otherwise it is zero.

The remaining three values in the array are the ResponseHeader, key, and value.

# File lib/dalli/protocol/meta/response_processor.rb, line 137
def getk_response_from_buffer(buf)
  # There's no header in the buffer, so don't advance
  return [0, nil, nil, nil, nil] unless contains_header?(buf)

  tokens, header_len, body_len = tokens_from_header_buffer(buf)

  # We have a complete response that has no body.
  # This is either the response to the terminating
  # noop or, if the status is not MN, an intermediate
  # error response that needs to be discarded.
  return [header_len, true, nil, nil, nil] if body_len.zero?

  resp_size = header_len + body_len + TERMINATOR.length
  # The header is in the buffer, but the body is not.  As we don't have
  # a complete response, don't advance the buffer
  return [0, nil, nil, nil, nil] unless buf.bytesize >= resp_size

  # The full response is in our buffer, so parse it and return
  # the values
  body = buf.slice(header_len, body_len)
  full_response_from_buffer(tokens, body, resp_size)
end
header_from_buffer(buf) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 164
def header_from_buffer(buf)
  buf.split(TERMINATOR, 2).first
end
key_from_tokens(tokens) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 183
def key_from_tokens(tokens)
  encoded_key = value_from_tokens(tokens, 'k')
  base64_encoded = tokens.any?('b')
  KeyRegularizer.decode(encoded_key, base64_encoded)
end
meta_delete() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 67
def meta_delete
  tokens = error_on_unexpected!([HD, NF, EX])
  tokens.first == HD
end
meta_get_with_value(cache_nils: false) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 30
def meta_get_with_value(cache_nils: false)
  tokens = error_on_unexpected!([VA, EN, HD])
  return cache_nils ? ::Dalli::NOT_FOUND : nil if tokens.first == EN
  return true unless tokens.first == VA

  @value_marshaller.retrieve(read_line, bitflags_from_tokens(tokens))
end
meta_get_with_value_and_cas() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 38
def meta_get_with_value_and_cas
  tokens = error_on_unexpected!([VA, EN, HD])
  return [nil, 0] if tokens.first == EN

  cas = cas_from_tokens(tokens)
  return [nil, cas] unless tokens.first == VA

  [@value_marshaller.retrieve(read_line, bitflags_from_tokens(tokens)), cas]
end
meta_get_without_value() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 48
def meta_get_without_value
  tokens = error_on_unexpected!([EN, HD])
  tokens.first == EN ? nil : true
end
meta_set_append_prepend() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 60
def meta_set_append_prepend
  tokens = error_on_unexpected!([HD, NS, NF, EX])
  return false unless tokens.first == HD

  true
end
meta_set_with_cas() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 53
def meta_set_with_cas
  tokens = error_on_unexpected!([HD, NS, NF, EX])
  return false unless tokens.first == HD

  cas_from_tokens(tokens)
end
next_line_to_tokens() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 204
def next_line_to_tokens
  line = read_line
  line&.split || []
end
read_line() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 200
def read_line
  @io_source.read_line&.chomp!(TERMINATOR)
end
reset() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 96
def reset
  error_on_unexpected!([RESET])

  true
end
stats() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 80
def stats
  tokens = error_on_unexpected!([END_TOKEN, STAT])
  values = {}
  while tokens.first != END_TOKEN
    values[tokens[1]] = tokens[2]
    tokens = next_line_to_tokens
  end
  values
end
tokens_from_header_buffer(buf) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 114
def tokens_from_header_buffer(buf)
  header = header_from_buffer(buf)
  tokens = header.split
  header_len = header.bytesize + TERMINATOR.length
  body_len = body_len_from_tokens(tokens)
  [tokens, header_len, body_len]
end
value_from_tokens(tokens, flag) click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 193
def value_from_tokens(tokens, flag)
  bitflags_token = tokens.find { |t| t.start_with?(flag) }
  return 0 unless bitflags_token

  bitflags_token[1..-1]
end
version() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 102
def version
  tokens = error_on_unexpected!([VERSION])
  tokens.last
end