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 24
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 174
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 188
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 178
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 106
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 159
def contains_header?(buf)
  buf.include?(TERMINATOR)
end
decr_incr() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 71
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 167
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 89
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 121
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 136
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 163
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 182
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 66
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 29
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 37
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 47
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 59
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 52
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 203
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 199
def read_line
  @io_source.read_line&.chomp!(TERMINATOR)
end
reset() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 95
def reset
  error_on_unexpected!([RESET])

  true
end
stats() click to toggle source
# File lib/dalli/protocol/meta/response_processor.rb, line 79
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 113
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 192
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 101
def version
  tokens = error_on_unexpected!([VERSION])
  tokens.last
end